a64multienc.c
Go to the documentation of this file.
1 /*
2  * a64 video encoder - multicolor modes
3  * Copyright (c) 2009 Tobias Bindhammer
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 /**
23  * @file
24  * a64 video encoder - multicolor modes
25  */
26 
27 #include "a64colors.h"
28 #include "a64tables.h"
29 #include "elbg.h"
30 #include "internal.h"
31 #include "libavutil/common.h"
32 #include "libavutil/intreadwrite.h"
33 
34 #define DITHERSTEPS 8
35 #define CHARSET_CHARS 256
36 #define INTERLACED 1
37 #define CROP_SCREENS 1
38 
39 #define C64XRES 320
40 #define C64YRES 200
41 
42 typedef struct A64Context {
43  /* general variables */
45 
46  /* variables for multicolor modes */
50  unsigned mc_frame_counter;
52  int *mc_charmap;
53  int *mc_best_cb;
54  int mc_luma_vals[5];
59 
60  /* pts of the next packet that will be output */
61  int64_t next_pts;
62 } A64Context;
63 
64 /* gray gradient */
65 static const int mc_colors[5]={0x0,0xb,0xc,0xf,0x1};
66 
67 /* other possible gradients - to be tested */
68 //static const int mc_colors[5]={0x0,0x8,0xa,0xf,0x7};
69 //static const int mc_colors[5]={0x0,0x9,0x8,0xa,0x3};
70 
71 static void to_meta_with_crop(AVCodecContext *avctx, AVFrame *p, int *dest)
72 {
73  int blockx, blocky, x, y;
74  int luma = 0;
75  int height = FFMIN(avctx->height, C64YRES);
76  int width = FFMIN(avctx->width , C64XRES);
77  uint8_t *src = p->data[0];
78 
79  for (blocky = 0; blocky < C64YRES; blocky += 8) {
80  for (blockx = 0; blockx < C64XRES; blockx += 8) {
81  for (y = blocky; y < blocky + 8 && y < C64YRES; y++) {
82  for (x = blockx; x < blockx + 8 && x < C64XRES; x += 2) {
83  if(x < width && y < height) {
84  /* build average over 2 pixels */
85  luma = (src[(x + 0 + y * p->linesize[0])] +
86  src[(x + 1 + y * p->linesize[0])]) / 2;
87  /* write blocks as linear data now so they are suitable for elbg */
88  dest[0] = luma;
89  }
90  dest++;
91  }
92  }
93  }
94  }
95 }
96 
97 static void render_charset(AVCodecContext *avctx, uint8_t *charset,
98  uint8_t *colrammap)
99 {
100  A64Context *c = avctx->priv_data;
101  uint8_t row1, row2;
102  int charpos, x, y;
103  int a, b;
104  uint8_t pix;
105  int lowdiff, highdiff;
106  int *best_cb = c->mc_best_cb;
107  static uint8_t index1[256];
108  static uint8_t index2[256];
109  static uint8_t dither[256];
110  int i;
111  int distance;
112 
113  /* generate lookup-tables for dither and index before looping */
114  i = 0;
115  for (a=0; a < 256; a++) {
116  if(i < c->mc_pal_size -1 && a == c->mc_luma_vals[i + 1]) {
117  distance = c->mc_luma_vals[i + 1] - c->mc_luma_vals[i];
118  for(b = 0; b <= distance; b++) {
119  dither[c->mc_luma_vals[i] + b] = b * (DITHERSTEPS - 1) / distance;
120  }
121  i++;
122  }
123  if(i >= c->mc_pal_size - 1) dither[a] = 0;
124  index1[a] = i;
125  index2[a] = FFMIN(i + 1, c->mc_pal_size - 1);
126  }
127 
128  /* and render charset */
129  for (charpos = 0; charpos < CHARSET_CHARS; charpos++) {
130  lowdiff = 0;
131  highdiff = 0;
132  for (y = 0; y < 8; y++) {
133  row1 = 0; row2 = 0;
134  for (x = 0; x < 4; x++) {
135  pix = best_cb[y * 4 + x];
136 
137  /* accumulate error for brightest/darkest color */
138  if (index1[pix] >= 3)
139  highdiff += pix - c->mc_luma_vals[3];
140  if (index1[pix] < 1)
141  lowdiff += c->mc_luma_vals[1] - pix;
142 
143  row1 <<= 2;
144 
145  if (INTERLACED) {
146  row2 <<= 2;
147  if (interlaced_dither_patterns[dither[pix]][(y & 3) * 2 + 0][x & 3])
148  row1 |= 3-(index2[pix] & 3);
149  else
150  row1 |= 3-(index1[pix] & 3);
151 
152  if (interlaced_dither_patterns[dither[pix]][(y & 3) * 2 + 1][x & 3])
153  row2 |= 3-(index2[pix] & 3);
154  else
155  row2 |= 3-(index1[pix] & 3);
156  }
157  else {
158  if (multi_dither_patterns[dither[pix]][(y & 3)][x & 3])
159  row1 |= 3-(index2[pix] & 3);
160  else
161  row1 |= 3-(index1[pix] & 3);
162  }
163  }
164  charset[y+0x000] = row1;
165  if (INTERLACED) charset[y+0x800] = row2;
166  }
167  /* do we need to adjust pixels? */
168  if (highdiff > 0 && lowdiff > 0 && c->mc_use_5col) {
169  if (lowdiff > highdiff) {
170  for (x = 0; x < 32; x++)
171  best_cb[x] = FFMIN(c->mc_luma_vals[3], best_cb[x]);
172  } else {
173  for (x = 0; x < 32; x++)
174  best_cb[x] = FFMAX(c->mc_luma_vals[1], best_cb[x]);
175  }
176  charpos--; /* redo now adjusted char */
177  /* no adjustment needed, all fine */
178  } else {
179  /* advance pointers */
180  best_cb += 32;
181  charset += 8;
182 
183  /* remember colorram value */
184  colrammap[charpos] = (highdiff > 0);
185  }
186  }
187 }
188 
190 {
191  A64Context *c = avctx->priv_data;
193  av_free(c->mc_best_cb);
194  av_free(c->mc_charset);
195  av_free(c->mc_charmap);
196  av_free(c->mc_colram);
197  return 0;
198 }
199 
201 {
202  A64Context *c = avctx->priv_data;
203  int a;
204  av_lfg_init(&c->randctx, 1);
205 
206  if (avctx->global_quality < 1) {
207  c->mc_lifetime = 4;
208  } else {
209  c->mc_lifetime = avctx->global_quality /= FF_QP2LAMBDA;
210  }
211 
212  av_log(avctx, AV_LOG_INFO, "charset lifetime set to %d frame(s)\n", c->mc_lifetime);
213 
214  c->mc_frame_counter = 0;
215  c->mc_use_5col = avctx->codec->id == AV_CODEC_ID_A64_MULTI5;
216  c->mc_pal_size = 4 + c->mc_use_5col;
217 
218  /* precalc luma values for later use */
219  for (a = 0; a < c->mc_pal_size; a++) {
220  c->mc_luma_vals[a]=a64_palette[mc_colors[a]][0] * 0.30 +
221  a64_palette[mc_colors[a]][1] * 0.59 +
222  a64_palette[mc_colors[a]][2] * 0.11;
223  }
224 
225  if (!(c->mc_meta_charset = av_malloc(32000 * c->mc_lifetime * sizeof(int))) ||
226  !(c->mc_best_cb = av_malloc(CHARSET_CHARS * 32 * sizeof(int))) ||
227  !(c->mc_charmap = av_mallocz(1000 * c->mc_lifetime * sizeof(int))) ||
228  !(c->mc_colram = av_mallocz(CHARSET_CHARS * sizeof(uint8_t))) ||
229  !(c->mc_charset = av_malloc(0x800 * (INTERLACED+1) * sizeof(uint8_t)))) {
230  av_log(avctx, AV_LOG_ERROR, "Failed to allocate buffer memory.\n");
231  return AVERROR(ENOMEM);
232  }
233 
234  /* set up extradata */
235  if (!(avctx->extradata = av_mallocz(8 * 4 + FF_INPUT_BUFFER_PADDING_SIZE))) {
236  av_log(avctx, AV_LOG_ERROR, "Failed to allocate memory for extradata.\n");
237  return AVERROR(ENOMEM);
238  }
239  avctx->extradata_size = 8 * 4;
240  AV_WB32(avctx->extradata, c->mc_lifetime);
241  AV_WB32(avctx->extradata + 16, INTERLACED);
242 
244  avctx->coded_frame = &c->picture;
246  avctx->coded_frame->key_frame = 1;
247  if (!avctx->codec_tag)
248  avctx->codec_tag = AV_RL32("a64m");
249 
251 
252  return 0;
253 }
254 
255 static void a64_compress_colram(unsigned char *buf, int *charmap, uint8_t *colram)
256 {
257  int a;
258  uint8_t temp;
259  /* only needs to be done in 5col mode */
260  /* XXX could be squeezed to 0x80 bytes */
261  for (a = 0; a < 256; a++) {
262  temp = colram[charmap[a + 0x000]] << 0;
263  temp |= colram[charmap[a + 0x100]] << 1;
264  temp |= colram[charmap[a + 0x200]] << 2;
265  if (a < 0xe8) temp |= colram[charmap[a + 0x300]] << 3;
266  buf[a] = temp << 2;
267  }
268 }
269 
271  const AVFrame *pict, int *got_packet)
272 {
273  A64Context *c = avctx->priv_data;
274  AVFrame *const p = &c->picture;
275 
276  int frame;
277  int x, y;
278  int b_height;
279  int b_width;
280 
281  int req_size, ret;
282  uint8_t *buf = NULL;
283 
284  int *charmap = c->mc_charmap;
285  uint8_t *colram = c->mc_colram;
286  uint8_t *charset = c->mc_charset;
287  int *meta = c->mc_meta_charset;
288  int *best_cb = c->mc_best_cb;
289 
290  int charset_size = 0x800 * (INTERLACED + 1);
291  int colram_size = 0x100 * c->mc_use_5col;
292  int screen_size;
293 
294  if(CROP_SCREENS) {
295  b_height = FFMIN(avctx->height,C64YRES) >> 3;
296  b_width = FFMIN(avctx->width ,C64XRES) >> 3;
297  screen_size = b_width * b_height;
298  } else {
299  b_height = C64YRES >> 3;
300  b_width = C64XRES >> 3;
301  screen_size = 0x400;
302  }
303 
304  /* no data, means end encoding asap */
305  if (!pict) {
306  /* all done, end encoding */
307  if (!c->mc_lifetime) return 0;
308  /* no more frames in queue, prepare to flush remaining frames */
309  if (!c->mc_frame_counter) {
310  c->mc_lifetime = 0;
311  }
312  /* still frames in queue so limit lifetime to remaining frames */
313  else c->mc_lifetime = c->mc_frame_counter;
314  /* still new data available */
315  } else {
316  /* fill up mc_meta_charset with data until lifetime exceeds */
317  if (c->mc_frame_counter < c->mc_lifetime) {
318  *p = *pict;
320  p->key_frame = 1;
321  to_meta_with_crop(avctx, p, meta + 32000 * c->mc_frame_counter);
322  c->mc_frame_counter++;
323  if (c->next_pts == AV_NOPTS_VALUE)
324  c->next_pts = pict->pts;
325  /* lifetime is not reached so wait for next frame first */
326  return 0;
327  }
328  }
329 
330  /* lifetime reached so now convert X frames at once */
331  if (c->mc_frame_counter == c->mc_lifetime) {
332  req_size = 0;
333  /* any frames to encode? */
334  if (c->mc_lifetime) {
335  req_size = charset_size + c->mc_lifetime*(screen_size + colram_size);
336  if ((ret = ff_alloc_packet2(avctx, pkt, req_size)) < 0)
337  return ret;
338  buf = pkt->data;
339 
340  /* calc optimal new charset + charmaps */
341  ff_init_elbg(meta, 32, 1000 * c->mc_lifetime, best_cb, CHARSET_CHARS, 50, charmap, &c->randctx);
342  ff_do_elbg (meta, 32, 1000 * c->mc_lifetime, best_cb, CHARSET_CHARS, 50, charmap, &c->randctx);
343 
344  /* create colorram map and a c64 readable charset */
345  render_charset(avctx, charset, colram);
346 
347  /* copy charset to buf */
348  memcpy(buf, charset, charset_size);
349 
350  /* advance pointers */
351  buf += charset_size;
352  charset += charset_size;
353  }
354 
355  /* write x frames to buf */
356  for (frame = 0; frame < c->mc_lifetime; frame++) {
357  /* copy charmap to buf. buf is uchar*, charmap is int*, so no memcpy here, sorry */
358  for (y = 0; y < b_height; y++) {
359  for (x = 0; x < b_width; x++) {
360  buf[y * b_width + x] = charmap[y * b_width + x];
361  }
362  }
363  /* advance pointers */
364  buf += screen_size;
365  req_size += screen_size;
366 
367  /* compress and copy colram to buf */
368  if (c->mc_use_5col) {
369  a64_compress_colram(buf, charmap, colram);
370  /* advance pointers */
371  buf += colram_size;
372  req_size += colram_size;
373  }
374 
375  /* advance to next charmap */
376  charmap += 1000;
377  }
378 
379  AV_WB32(avctx->extradata + 4, c->mc_frame_counter);
380  AV_WB32(avctx->extradata + 8, charset_size);
381  AV_WB32(avctx->extradata + 12, screen_size + colram_size);
382 
383  /* reset counter */
384  c->mc_frame_counter = 0;
385 
386  pkt->pts = pkt->dts = c->next_pts;
388 
389  pkt->size = req_size;
390  pkt->flags |= AV_PKT_FLAG_KEY;
391  *got_packet = !!req_size;
392  }
393  return 0;
394 }
395 
396 #if CONFIG_A64MULTI_ENCODER
397 AVCodec ff_a64multi_encoder = {
398  .name = "a64multi",
399  .type = AVMEDIA_TYPE_VIDEO,
400  .id = AV_CODEC_ID_A64_MULTI,
401  .priv_data_size = sizeof(A64Context),
403  .encode2 = a64multi_encode_frame,
405  .pix_fmts = (const enum AVPixelFormat[]) {AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE},
406  .long_name = NULL_IF_CONFIG_SMALL("Multicolor charset for Commodore 64"),
407  .capabilities = CODEC_CAP_DELAY,
408 };
409 #endif
410 #if CONFIG_A64MULTI5_ENCODER
411 AVCodec ff_a64multi5_encoder = {
412  .name = "a64multi5",
413  .type = AVMEDIA_TYPE_VIDEO,
415  .priv_data_size = sizeof(A64Context),
417  .encode2 = a64multi_encode_frame,
419  .pix_fmts = (const enum AVPixelFormat[]) {AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE},
420  .long_name = NULL_IF_CONFIG_SMALL("Multicolor charset for Commodore 64, extended with 5th color (colram)"),
421  .capabilities = CODEC_CAP_DELAY,
422 };
423 #endif
Definition: lfg.h:25
void * av_mallocz(size_t size)
Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if ...
Definition: mem.c:205
const struct AVCodec * codec
#define C64XRES
Definition: a64multienc.c:39
This structure describes decoded (raw) audio or video data.
Definition: frame.h:76
#define INTERLACED
Definition: a64multienc.c:36
else temp
Definition: vf_mcdeint.c:148
AVFrame * coded_frame
the picture in the bitstream
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:35
uint8_t * mc_palette
Definition: a64multienc.c:57
static av_cold int a64multi_init_encoder(AVCodecContext *avctx)
Definition: a64multienc.c:200
int mc_luma_vals[5]
Definition: a64multienc.c:54
#define AV_WB32(p, darg)
Definition: intreadwrite.h:265
uint8_t
#define av_cold
Definition: attributes.h:78
static AVPacket pkt
Definition: demuxing.c:56
#define b
Definition: input.c:42
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition: frame.h:159
uint8_t * extradata
some codecs need / can use extradata like Huffman tables.
int mc_use_5col
Definition: a64multienc.c:49
uint8_t * data
int * mc_best_cb
Definition: a64multienc.c:53
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
frame
Definition: stft.m:14
Discrete Time axis x
enum AVCodecID id
void av_free(void *ptr)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc(). ...
Definition: mem.c:183
struct A64Context A64Context
#define CODEC_CAP_DELAY
Encoder or decoder requires flushing with NULL input at the end in order to give the complete and cor...
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
#define CROP_SCREENS
Definition: a64multienc.c:37
a64 video encoder - tables used by a64 encoders
void av_log(void *avcl, int level, const char *fmt,...)
Definition: log.c:246
const char * name
Name of the codec implementation.
#define DITHERSTEPS
Definition: a64multienc.c:34
static void to_meta_with_crop(AVCodecContext *avctx, AVFrame *p, int *dest)
Definition: a64multienc.c:71
AVFrame picture
Definition: a64multienc.c:44
#define FFMAX(a, b)
Definition: common.h:56
int flags
A combination of AV_PKT_FLAG values.
#define C64YRES
Definition: a64multienc.c:40
static float distance(float x, float y, int band)
#define FF_INPUT_BUFFER_PADDING_SIZE
Required number of additionally allocated bytes at the end of the input bitstream for decoding...
void ff_do_elbg(int *points, int dim, int numpoints, int *codebook, int numCB, int max_steps, int *closest_cb, AVLFG *rand_state)
Implementation of the Enhanced LBG Algorithm Based on the paper "Neural Networks 14:1219-1237" that c...
Definition: elbg.c:354
int * mc_meta_charset
Definition: a64multienc.c:51
#define CHARSET_CHARS
Definition: a64multienc.c:35
enum AVPictureType pict_type
Picture type of the frame.
Definition: frame.h:144
#define FFMIN(a, b)
Definition: common.h:58
unsigned mc_frame_counter
Definition: a64multienc.c:50
static void a64_compress_colram(unsigned char *buf, int *charmap, uint8_t *colram)
Definition: a64multienc.c:255
ret
Definition: avfilter.c:821
int width
picture width / height.
uint8_t * mc_charset
Definition: a64multienc.c:55
#define AV_RL32
int ff_alloc_packet2(AVCodecContext *avctx, AVPacket *avpkt, int size)
Check AVPacket size and/or allocate data.
int mc_pal_size
Definition: a64multienc.c:58
NULL
Definition: eval.c:55
static int width
Definition: tests/utils.c:158
dest
Definition: start.py:60
static void render_charset(AVCodecContext *avctx, uint8_t *charset, uint8_t *colrammap)
Definition: a64multienc.c:97
AVS_Value src
Definition: avisynth_c.h:523
int linesize[AV_NUM_DATA_POINTERS]
For video, size in bytes of each picture line.
Definition: frame.h:101
main external API structure.
static void close(AVCodecParserContext *s)
Definition: h264_parser.c:375
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:148
unsigned int codec_tag
fourcc (LSB first, so "ABCD" -> (&#39;D&#39;<<24) + (&#39;C&#39;<<16) + (&#39;B&#39;<<8) + &#39;A&#39;).
void * buf
Definition: avisynth_c.h:594
BYTE int const BYTE int int int height
Definition: avisynth_c.h:713
void * av_malloc(size_t size)
Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if ...
Definition: mem.c:73
void avcodec_get_frame_defaults(AVFrame *frame)
Set the fields of the given AVFrame to default values.
a64 video encoder - c64 colors in rgb
synthesis window for stochastic i
static int a64multi_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *pict, int *got_packet)
Definition: a64multienc.c:270
static const int mc_colors[5]
Definition: a64multienc.c:65
av_cold void av_lfg_init(AVLFG *c, unsigned int seed)
Definition: lfg.c:30
void ff_init_elbg(int *points, int dim, int numpoints, int *codebook, int numCB, int max_steps, int *closest_cb, AVLFG *rand_state)
Initialize the **codebook vector for the elbg algorithm.
Definition: elbg.c:327
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFilterBuffer structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Buffer references ownership and permissions
int global_quality
Global quality for codecs which cannot change it per frame.
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:87
int64_t next_pts
Definition: a64multienc.c:61
Y , 8bpp.
Definition: pixfmt.h:76
common internal api header.
common internal and external API header
int * mc_charmap
Definition: a64multienc.c:52
static double c[64]
function y
Definition: D.m:1
static const uint8_t a64_palette[16][3]
Definition: a64colors.h:33
static const uint8_t interlaced_dither_patterns[9][8][4]
Definition: a64tables.h:93
AVLFG randctx
Definition: a64multienc.c:47
int key_frame
1 -> keyframe, 0-> not
Definition: frame.h:139
#define FF_QP2LAMBDA
factor to convert from H.263 QP to lambda
Definition: avutil.h:169
static av_cold int a64multi_close_encoder(AVCodecContext *avctx)
Definition: a64multienc.c:189
int64_t dts
Decompression timestamp in AVStream->time_base units; the time at which the packet is decompressed...
#define AV_LOG_INFO
Definition: log.h:156
uint8_t * mc_colram
Definition: a64multienc.c:56
int mc_lifetime
Definition: a64multienc.c:48
AVPixelFormat
Pixel format.
Definition: pixfmt.h:66
This structure stores compressed data.
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition: avutil.h:190
static const uint8_t multi_dither_patterns[9][4][4]
dither patterns used vor rendering the multicolor charset
Definition: a64tables.h:36