From e2f402f160cf574cc69431f4a40a60078cf5e911 Mon Sep 17 00:00:00 2001 From: Tobias Bindhammer Date: Tue, 24 Aug 2010 15:21:27 +0000 Subject: [PATCH] added interlacing option and compression option for colorram (lut) Originally committed as revision 24901 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavcodec/a64colors.h | 2 +- libavcodec/a64enc.h | 3 +- libavcodec/a64multienc.c | 167 +++++++++++++++++++++++++++++---------- libavcodec/a64tables.h | 57 +++++++++++++ 4 files changed, 184 insertions(+), 45 deletions(-) diff --git a/libavcodec/a64colors.h b/libavcodec/a64colors.h index 5928c8d453..1a83fb12e2 100644 --- a/libavcodec/a64colors.h +++ b/libavcodec/a64colors.h @@ -1,5 +1,5 @@ /* - * a64 video encoder - tables used by 64enc + * a64 video encoder - c64 colors in rgb (Pepto) * Copyright (c) 2009 Tobias Bindhammer * * This file is part of FFmpeg. diff --git a/libavcodec/a64enc.h b/libavcodec/a64enc.h index a04a819616..caa4891e09 100644 --- a/libavcodec/a64enc.h +++ b/libavcodec/a64enc.h @@ -45,8 +45,9 @@ typedef struct A64Context { int *mc_meta_charset; int *mc_charmap; int *mc_best_cb; - int *mc_charset; int mc_luma_vals[5]; + uint8_t *mc_charset; + uint8_t *mc_colram; } A64Context; #endif /* AVCODEC_A64ENC_H */ diff --git a/libavcodec/a64multienc.c b/libavcodec/a64multienc.c index ec2af9954b..9deab93ff2 100644 --- a/libavcodec/a64multienc.c +++ b/libavcodec/a64multienc.c @@ -32,10 +32,16 @@ #define DITHERSTEPS 8 #define CHARSET_CHARS 256 +#define INTERLACED 1 +#define LIFETIME 4 /* gray gradient */ static const int mc_colors[5]={0x0,0xb,0xc,0xf,0x1}; +/* other possible gradients - to be tested */ +//static const int mc_colors[5]={0x0,0x8,0xa,0xf,0x7}; +//static const int mc_colors[5]={0x0,0x9,0x8,0xa,0x3}; + static void to_meta_with_crop(AVCodecContext *avctx, AVFrame *p, int *dest) { int blockx, blocky, x, y; @@ -66,7 +72,7 @@ static void render_charset(AVCodecContext *avctx, uint8_t *charset, uint8_t *colrammap) { A64Context *c = avctx->priv_data; - uint8_t row1; + uint8_t row1, row2; int charpos, x, y; int a, b; uint8_t pix; @@ -97,7 +103,7 @@ static void render_charset(AVCodecContext *avctx, uint8_t *charset, lowdiff = 0; highdiff = 0; for (y = 0; y < 8; y++) { - row1 = 0; + row1 = 0; row2 = 0; for (x = 0; x < 4; x++) { pix = best_cb[y * 4 + x]; @@ -109,12 +115,27 @@ static void render_charset(AVCodecContext *avctx, uint8_t *charset, row1 <<= 2; - if (multi_dither_patterns[dither[pix]][(y & 3)][x & 3]) - row1 |= 3-(index2[pix] & 3); - else - row1 |= 3-(index1[pix] & 3); + if(INTERLACED) { + row2 <<= 2; + if (interlaced_dither_patterns[dither[pix]][(y & 3) * 2 + 0][x & 3]) + row1 |= 3-(index2[pix] & 3); + else + row1 |= 3-(index1[pix] & 3); + + if (interlaced_dither_patterns[dither[pix]][(y & 3) * 2 + 1][x & 3]) + row2 |= 3-(index2[pix] & 3); + else + row2 |= 3-(index1[pix] & 3); + } + else { + if (multi_dither_patterns[dither[pix]][(y & 3)][x & 3]) + row1 |= 3-(index2[pix] & 3); + else + row1 |= 3-(index1[pix] & 3); + } } charset[y+0x000] = row1; + if(INTERLACED) charset[y+0x800] = row2; } /* do we need to adjust pixels? */ if (highdiff > 0 && lowdiff > 0) { @@ -133,7 +154,7 @@ static void render_charset(AVCodecContext *avctx, uint8_t *charset, charset += 8; /* remember colorram value */ - colrammap[charpos] = (highdiff > 0) + 8; + colrammap[charpos] = (highdiff > 0); } } } @@ -143,8 +164,9 @@ static av_cold int a64multi_close_encoder(AVCodecContext *avctx) A64Context *c = avctx->priv_data; av_free(c->mc_meta_charset); av_free(c->mc_best_cb); - av_free(c->mc_charmap); av_free(c->mc_charset); + av_free(c->mc_charmap); + av_free(c->mc_colram); return 0; } @@ -171,10 +193,11 @@ static av_cold int a64multi_init_encoder(AVCodecContext *avctx) c->mc_frame_counter = 0; c->mc_use_5col = avctx->codec->id == CODEC_ID_A64_MULTI5; - c->mc_meta_charset = av_malloc(32000 * c->mc_lifetime * sizeof(int)); - c->mc_best_cb = av_malloc(CHARSET_CHARS * 32 * sizeof(int)); - c->mc_charmap = av_malloc(1000 * c->mc_lifetime * sizeof(int)); - c->mc_charset = av_malloc(0x800 * sizeof(uint8_t)); + c->mc_meta_charset = av_malloc (32000 * c->mc_lifetime * sizeof(int)); + c->mc_best_cb = av_malloc (CHARSET_CHARS * 32 * sizeof(int)); + c->mc_charmap = av_mallocz(1000 * c->mc_lifetime * sizeof(int)); + c->mc_colram = av_mallocz(CHARSET_CHARS * sizeof(uint8_t)); + c->mc_charset = av_malloc (0x800 * (INTERLACED+1) * sizeof(uint8_t)); avcodec_get_frame_defaults(&c->picture); avctx->coded_frame = &c->picture; @@ -186,6 +209,21 @@ static av_cold int a64multi_init_encoder(AVCodecContext *avctx) return 0; } +static void a64_compress_colram(unsigned char *buf, int *charmap, uint8_t *colram) +{ + int a; + uint8_t temp; + /* only needs to be done in 5col mode */ + /* XXX could be squeezed to 0x80 bytes */ + for (a = 0; a < 256; a++) { + temp = colram[charmap[a + 0x000]] << 0; + temp |= colram[charmap[a + 0x100]] << 1; + temp |= colram[charmap[a + 0x200]] << 2; + if(a < 0xe8) temp |= colram[charmap[a + 0x300]] << 3; + buf[a] = temp << 2; + } +} + static int a64multi_encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_size, void *data) { @@ -196,49 +234,92 @@ static int a64multi_encode_frame(AVCodecContext *avctx, unsigned char *buf, int frame; int a; - uint8_t colrammap[256]; - int *charmap = c->mc_charmap; - int *meta = c->mc_meta_charset; - int *best_cb = c->mc_best_cb; - int frm_size = 0x400 + 0x400 * c->mc_use_5col; int req_size; + int num_frames = LIFETIME; - /* it is the last frame so prepare to flush */ - if (!data) - c->mc_lifetime = c->mc_frame_counter; + int *charmap = c->mc_charmap; + uint8_t *colram = c->mc_colram; + uint8_t *charset = c->mc_charset; + int *meta = c->mc_meta_charset; + int *best_cb = c->mc_best_cb; - req_size = 0x800 + frm_size * c->mc_lifetime; - - if (req_size > buf_size) { - av_log(avctx, AV_LOG_ERROR, "buf size too small (need %d, got %d)\n", req_size, buf_size); - return AVERROR(EINVAL); + /* no data, means end encoding asap */ + if (!data) { + /* all done, end encoding */ + if(!c->mc_lifetime) return 0; + /* no more frames in queue, prepare to flush remaining frames */ + if(!c->mc_frame_counter) { + num_frames=c->mc_lifetime; + c->mc_lifetime=0; + } + /* still frames in queue so limit lifetime to remaining frames */ + else c->mc_lifetime=c->mc_frame_counter; } - /* fill up mc_meta_charset with framedata until lifetime exceeds */ - if (c->mc_frame_counter < c->mc_lifetime) { - *p = *pict; - p->pict_type = FF_I_TYPE; - p->key_frame = 1; - to_meta_with_crop(avctx, p, meta + 32000 * c->mc_frame_counter); - c->mc_frame_counter++; - /* lifetime is not reached */ - return 0; + /* still new data available */ + else { + /* fill up mc_meta_charset with data until lifetime exceeds */ + if (c->mc_frame_counter < c->mc_lifetime) { + *p = *pict; + p->pict_type = FF_I_TYPE; + p->key_frame = 1; + to_meta_with_crop(avctx, p, meta + 32000 * c->mc_frame_counter); + c->mc_frame_counter++; + /* lifetime is not reached so wait for next frame first */ + return 0; + } } - /* lifetime exceeded so now convert X frames at once */ - if (c->mc_frame_counter == c->mc_lifetime && c->mc_lifetime > 0) { - c->mc_frame_counter = 0; - ff_init_elbg(meta, 32, 1000 * c-> mc_lifetime, best_cb, CHARSET_CHARS, 5, charmap, &c->randctx); - ff_do_elbg (meta, 32, 1000 * c-> mc_lifetime, best_cb, CHARSET_CHARS, 5, charmap, &c->randctx); - render_charset(avctx, buf, colrammap); + /* lifetime reached so now convert X frames at once */ + if (c->mc_frame_counter == c->mc_lifetime) { + /* any frames to encode? */ + if(c->mc_lifetime) { + /* calc optimal new charset + charmaps */ + ff_init_elbg(meta, 32, 1000 * c->mc_lifetime, best_cb, CHARSET_CHARS, 50, charmap, &c->randctx); + ff_do_elbg (meta, 32, 1000 * c->mc_lifetime, best_cb, CHARSET_CHARS, 50, charmap, &c->randctx); + /* create colorram map and a c64 readable charset */ + render_charset(avctx, charset, colram); + } + + req_size = 0; + + //XXX this all should maybe move to the muxer? as well as teh chunked/not chunked thing? + /* either write charset as a whole (more comfy when playing from mem) */ + /* copy charset chunk if exists */ + if(c->mc_lifetime) { + memcpy(buf,charset,0x800*(INTERLACED+1)); + buf += 0x800*(INTERLACED+1); + charset += 0x800*(INTERLACED+1); + req_size += 0x800*(INTERLACED+1); + } + else memset(buf,0,0x800*(INTERLACED+1)); + + /* write x frames to buf */ for (frame = 0; frame < c->mc_lifetime; frame++) { + /* buf is uchar*, charmap is int*, so no memcpy here, sorry */ for (a = 0; a < 1000; a++) { - buf[0x800 + a] = charmap[a]; - if (c->mc_use_5col) buf[0xc00 + a] = colrammap[charmap[a]]; + buf[a] = charmap[a]; } - buf += frm_size; + buf += 0x400; + req_size += 0x400; + + if(c->mc_use_5col) { + a64_compress_colram(buf,charmap,colram); + buf += 0x100; + req_size += 0x100; + } + + /* advance to next charmap */ charmap += 1000; } + + /* reset counter */ + c->mc_frame_counter = 0; + + if (req_size > buf_size) { + av_log(avctx, AV_LOG_ERROR, "buf size too small (need %d, got %d)\n", req_size, buf_size); + return -1; + } return req_size; } return 0; diff --git a/libavcodec/a64tables.h b/libavcodec/a64tables.h index 7ce60b0e5d..a955ef4caa 100644 --- a/libavcodec/a64tables.h +++ b/libavcodec/a64tables.h @@ -90,4 +90,61 @@ static const uint8_t multi_dither_patterns[9][4][4] = { }, }; +static const uint8_t interlaced_dither_patterns[9][8][4] = { + { + {0, 0, 0, 0}, {0, 0, 0, 0}, + {0, 0, 0, 0}, {0, 0, 0, 0}, + {0, 0, 0, 0}, {0, 0, 0, 0}, + {0, 0, 0, 0}, {0, 0, 0, 0}, + }, + { + {1, 0, 1, 0}, {0, 0, 0, 0}, + {0, 0, 0, 0}, {0, 0, 0, 0}, + {1, 0, 1, 0}, {0, 0, 0, 0}, + {0, 0, 0, 0}, {0, 0, 0, 0}, + }, + { + {1, 0, 1, 0}, {0, 0, 0, 0}, + {0, 0, 0, 0}, {0, 1, 0, 1}, + {1, 0, 1, 0}, {0, 0, 0, 0}, + {0, 0, 0, 0}, {0, 1, 0, 1}, + }, + { + {1, 0, 1, 0}, {0, 1, 0, 1}, + {0, 1, 0, 1}, {0, 0, 0, 0}, + {1, 0, 1, 0}, {0, 1, 0, 1}, + {0, 1, 0, 1}, {0, 0, 0, 0}, + }, + { + {1, 0, 1, 0}, {0, 1, 0, 1}, + {0, 1, 0, 1}, {1, 0, 1, 0}, + {1, 0, 1, 0}, {0, 1, 0, 1}, + {0, 1, 0, 1}, {1, 0, 1, 0}, + }, + { + {1, 0, 1, 0}, {0, 1, 0, 1}, + {1, 1, 1, 1}, {1, 0, 1, 0}, + {1, 0, 1, 0}, {0, 1, 0, 1}, + {1, 1, 1, 1}, {1, 0, 1, 0}, + }, + { + {1, 0, 1, 0}, {1, 1, 1, 1}, + {1, 1, 1, 1}, {0, 1, 0, 1}, + {1, 0, 1, 0}, {1, 1, 1, 1}, + {1, 1, 1, 1}, {0, 1, 0, 1}, + }, + { + {1, 1, 1, 1}, {1, 1, 1, 1}, + {1, 1, 1, 1}, {0, 1, 0, 1}, + {1, 1, 1, 1}, {1, 1, 1, 1}, + {1, 1, 1, 1}, {0, 1, 0, 1}, + }, + { + {1, 1, 1, 1}, {1, 1, 1, 1}, + {1, 1, 1, 1}, {1, 1, 1, 1}, + {1, 1, 1, 1}, {1, 1, 1, 1}, + {1, 1, 1, 1}, {1, 1, 1, 1}, + } +}; + #endif /* AVCODEC_A64TABLES_H */