• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • File List
  • Globals

libavcodec/a64multienc.c

Go to the documentation of this file.
00001 /*
00002  * a64 video encoder - multicolor modes
00003  * Copyright (c) 2009 Tobias Bindhammer
00004  *
00005  * This file is part of Libav.
00006  *
00007  * Libav is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * Libav is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with Libav; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00027 #include "a64enc.h"
00028 #include "a64colors.h"
00029 #include "a64tables.h"
00030 #include "elbg.h"
00031 #include "libavutil/intreadwrite.h"
00032 
00033 #define DITHERSTEPS   8
00034 #define CHARSET_CHARS 256
00035 #define INTERLACED    1
00036 #define CROP_SCREENS  1
00037 
00038 /* gray gradient */
00039 static const int mc_colors[5]={0x0,0xb,0xc,0xf,0x1};
00040 
00041 /* other possible gradients - to be tested */
00042 //static const int mc_colors[5]={0x0,0x8,0xa,0xf,0x7};
00043 //static const int mc_colors[5]={0x0,0x9,0x8,0xa,0x3};
00044 
00045 static void to_meta_with_crop(AVCodecContext *avctx, AVFrame *p, int *dest)
00046 {
00047     int blockx, blocky, x, y;
00048     int luma = 0;
00049     int height = FFMIN(avctx->height, C64YRES);
00050     int width  = FFMIN(avctx->width , C64XRES);
00051     uint8_t *src = p->data[0];
00052 
00053     for (blocky = 0; blocky < C64YRES; blocky += 8) {
00054         for (blockx = 0; blockx < C64XRES; blockx += 8) {
00055             for (y = blocky; y < blocky + 8 && y < C64YRES; y++) {
00056                 for (x = blockx; x < blockx + 8 && x < C64XRES; x += 2) {
00057                     if(x < width && y < height) {
00058                         /* build average over 2 pixels */
00059                         luma = (src[(x + 0 + y * p->linesize[0])] +
00060                                 src[(x + 1 + y * p->linesize[0])]) / 2;
00061                         /* write blocks as linear data now so they are suitable for elbg */
00062                         dest[0] = luma;
00063                     }
00064                     dest++;
00065                 }
00066             }
00067         }
00068     }
00069 }
00070 
00071 static void render_charset(AVCodecContext *avctx, uint8_t *charset,
00072                            uint8_t *colrammap)
00073 {
00074     A64Context *c = avctx->priv_data;
00075     uint8_t row1, row2;
00076     int charpos, x, y;
00077     int a, b;
00078     uint8_t pix;
00079     int lowdiff, highdiff;
00080     int *best_cb = c->mc_best_cb;
00081     static uint8_t index1[256];
00082     static uint8_t index2[256];
00083     static uint8_t dither[256];
00084     int i;
00085     int distance;
00086 
00087     /* generate lookup-tables for dither and index before looping */
00088     i = 0;
00089     for (a=0; a < 256; a++) {
00090         if(i < c->mc_pal_size -1 && a == c->mc_luma_vals[i + 1]) {
00091             distance = c->mc_luma_vals[i + 1] - c->mc_luma_vals[i];
00092             for(b = 0; b <= distance; b++) {
00093                   dither[c->mc_luma_vals[i] + b] = b * (DITHERSTEPS - 1) / distance;
00094             }
00095             i++;
00096         }
00097         if(i >= c->mc_pal_size - 1) dither[a] = 0;
00098         index1[a] = i;
00099         index2[a] = FFMIN(i + 1, c->mc_pal_size - 1);
00100     }
00101 
00102     /* and render charset */
00103     for (charpos = 0; charpos < CHARSET_CHARS; charpos++) {
00104         lowdiff  = 0;
00105         highdiff = 0;
00106         for (y = 0; y < 8; y++) {
00107             row1 = 0; row2 = 0;
00108             for (x = 0; x < 4; x++) {
00109                 pix = best_cb[y * 4 + x];
00110 
00111                 /* accumulate error for brightest/darkest color */
00112                 if (index1[pix] >= 3)
00113                     highdiff += pix - c->mc_luma_vals[3];
00114                 if (index1[pix] < 1)
00115                     lowdiff += c->mc_luma_vals[1] - pix;
00116 
00117                 row1 <<= 2;
00118 
00119                 if (INTERLACED) {
00120                     row2 <<= 2;
00121                     if (interlaced_dither_patterns[dither[pix]][(y & 3) * 2 + 0][x & 3])
00122                         row1 |= 3-(index2[pix] & 3);
00123                     else
00124                         row1 |= 3-(index1[pix] & 3);
00125 
00126                     if (interlaced_dither_patterns[dither[pix]][(y & 3) * 2 + 1][x & 3])
00127                         row2 |= 3-(index2[pix] & 3);
00128                     else
00129                         row2 |= 3-(index1[pix] & 3);
00130                 }
00131                 else {
00132                     if (multi_dither_patterns[dither[pix]][(y & 3)][x & 3])
00133                         row1 |= 3-(index2[pix] & 3);
00134                     else
00135                         row1 |= 3-(index1[pix] & 3);
00136                 }
00137             }
00138             charset[y+0x000] = row1;
00139             if (INTERLACED) charset[y+0x800] = row2;
00140         }
00141         /* do we need to adjust pixels? */
00142         if (highdiff > 0 && lowdiff > 0 && c->mc_use_5col) {
00143             if (lowdiff > highdiff) {
00144                 for (x = 0; x < 32; x++)
00145                     best_cb[x] = FFMIN(c->mc_luma_vals[3], best_cb[x]);
00146             } else {
00147                 for (x = 0; x < 32; x++)
00148                     best_cb[x] = FFMAX(c->mc_luma_vals[1], best_cb[x]);
00149             }
00150             charpos--;          /* redo now adjusted char */
00151         /* no adjustment needed, all fine */
00152         } else {
00153             /* advance pointers */
00154             best_cb += 32;
00155             charset += 8;
00156 
00157             /* remember colorram value */
00158             colrammap[charpos] = (highdiff > 0);
00159         }
00160     }
00161 }
00162 
00163 static av_cold int a64multi_close_encoder(AVCodecContext *avctx)
00164 {
00165     A64Context *c = avctx->priv_data;
00166     av_free(c->mc_meta_charset);
00167     av_free(c->mc_best_cb);
00168     av_free(c->mc_charset);
00169     av_free(c->mc_charmap);
00170     av_free(c->mc_colram);
00171     return 0;
00172 }
00173 
00174 static av_cold int a64multi_init_encoder(AVCodecContext *avctx)
00175 {
00176     A64Context *c = avctx->priv_data;
00177     int a;
00178     av_lfg_init(&c->randctx, 1);
00179 
00180     if (avctx->global_quality < 1) {
00181         c->mc_lifetime = 4;
00182     } else {
00183         c->mc_lifetime = avctx->global_quality /= FF_QP2LAMBDA;
00184     }
00185 
00186     av_log(avctx, AV_LOG_INFO, "charset lifetime set to %d frame(s)\n", c->mc_lifetime);
00187 
00188     c->mc_frame_counter = 0;
00189     c->mc_use_5col      = avctx->codec->id == CODEC_ID_A64_MULTI5;
00190     c->mc_pal_size      = 4 + c->mc_use_5col;
00191 
00192     /* precalc luma values for later use */
00193     for (a = 0; a < c->mc_pal_size; a++) {
00194         c->mc_luma_vals[a]=a64_palette[mc_colors[a]][0] * 0.30 +
00195                            a64_palette[mc_colors[a]][1] * 0.59 +
00196                            a64_palette[mc_colors[a]][2] * 0.11;
00197     }
00198 
00199     if (!(c->mc_meta_charset = av_malloc(32000 * c->mc_lifetime * sizeof(int))) ||
00200        !(c->mc_best_cb       = av_malloc(CHARSET_CHARS * 32 * sizeof(int)))     ||
00201        !(c->mc_charmap       = av_mallocz(1000 * c->mc_lifetime * sizeof(int))) ||
00202        !(c->mc_colram        = av_mallocz(CHARSET_CHARS * sizeof(uint8_t)))     ||
00203        !(c->mc_charset       = av_malloc(0x800 * (INTERLACED+1) * sizeof(uint8_t)))) {
00204         av_log(avctx, AV_LOG_ERROR, "Failed to allocate buffer memory.\n");
00205         return AVERROR(ENOMEM);
00206     }
00207 
00208     /* set up extradata */
00209     if (!(avctx->extradata = av_mallocz(8 * 4 + FF_INPUT_BUFFER_PADDING_SIZE))) {
00210         av_log(avctx, AV_LOG_ERROR, "Failed to allocate memory for extradata.\n");
00211         return AVERROR(ENOMEM);
00212     }
00213     avctx->extradata_size = 8 * 4;
00214     AV_WB32(avctx->extradata, c->mc_lifetime);
00215     AV_WB32(avctx->extradata + 16, INTERLACED);
00216 
00217     avcodec_get_frame_defaults(&c->picture);
00218     avctx->coded_frame            = &c->picture;
00219     avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I;
00220     avctx->coded_frame->key_frame = 1;
00221     if (!avctx->codec_tag)
00222          avctx->codec_tag = AV_RL32("a64m");
00223 
00224     return 0;
00225 }
00226 
00227 static void a64_compress_colram(unsigned char *buf, int *charmap, uint8_t *colram)
00228 {
00229     int a;
00230     uint8_t temp;
00231     /* only needs to be done in 5col mode */
00232     /* XXX could be squeezed to 0x80 bytes */
00233     for (a = 0; a < 256; a++) {
00234         temp  = colram[charmap[a + 0x000]] << 0;
00235         temp |= colram[charmap[a + 0x100]] << 1;
00236         temp |= colram[charmap[a + 0x200]] << 2;
00237         if (a < 0xe8) temp |= colram[charmap[a + 0x300]] << 3;
00238         buf[a] = temp << 2;
00239     }
00240 }
00241 
00242 static int a64multi_encode_frame(AVCodecContext *avctx, unsigned char *buf,
00243                                  int buf_size, void *data)
00244 {
00245     A64Context *c = avctx->priv_data;
00246     AVFrame *pict = data;
00247     AVFrame *const p = (AVFrame *) & c->picture;
00248 
00249     int frame;
00250     int x, y;
00251     int b_height;
00252     int b_width;
00253 
00254     int req_size;
00255 
00256     int *charmap     = c->mc_charmap;
00257     uint8_t *colram  = c->mc_colram;
00258     uint8_t *charset = c->mc_charset;
00259     int *meta        = c->mc_meta_charset;
00260     int *best_cb     = c->mc_best_cb;
00261 
00262     int charset_size = 0x800 * (INTERLACED + 1);
00263     int colram_size  = 0x100 * c->mc_use_5col;
00264     int screen_size;
00265 
00266     if(CROP_SCREENS) {
00267         b_height = FFMIN(avctx->height,C64YRES) >> 3;
00268         b_width  = FFMIN(avctx->width ,C64XRES) >> 3;
00269         screen_size = b_width * b_height;
00270     } else {
00271         b_height = C64YRES >> 3;
00272         b_width  = C64XRES >> 3;
00273         screen_size = 0x400;
00274     }
00275 
00276     /* no data, means end encoding asap */
00277     if (!data) {
00278         /* all done, end encoding */
00279         if (!c->mc_lifetime) return 0;
00280         /* no more frames in queue, prepare to flush remaining frames */
00281         if (!c->mc_frame_counter) {
00282             c->mc_lifetime = 0;
00283         }
00284         /* still frames in queue so limit lifetime to remaining frames */
00285         else c->mc_lifetime = c->mc_frame_counter;
00286     /* still new data available */
00287     } else {
00288         /* fill up mc_meta_charset with data until lifetime exceeds */
00289         if (c->mc_frame_counter < c->mc_lifetime) {
00290             *p = *pict;
00291             p->pict_type = AV_PICTURE_TYPE_I;
00292             p->key_frame = 1;
00293             to_meta_with_crop(avctx, p, meta + 32000 * c->mc_frame_counter);
00294             c->mc_frame_counter++;
00295             /* lifetime is not reached so wait for next frame first */
00296             return 0;
00297         }
00298     }
00299 
00300     /* lifetime reached so now convert X frames at once */
00301     if (c->mc_frame_counter == c->mc_lifetime) {
00302         req_size = 0;
00303         /* any frames to encode? */
00304         if (c->mc_lifetime) {
00305             /* calc optimal new charset + charmaps */
00306             ff_init_elbg(meta, 32, 1000 * c->mc_lifetime, best_cb, CHARSET_CHARS, 50, charmap, &c->randctx);
00307             ff_do_elbg  (meta, 32, 1000 * c->mc_lifetime, best_cb, CHARSET_CHARS, 50, charmap, &c->randctx);
00308 
00309             /* create colorram map and a c64 readable charset */
00310             render_charset(avctx, charset, colram);
00311 
00312             /* copy charset to buf */
00313             memcpy(buf,charset, charset_size);
00314 
00315             /* advance pointers */
00316             buf      += charset_size;
00317             charset  += charset_size;
00318             req_size += charset_size;
00319         }
00320         /* no charset so clean buf */
00321         else memset(buf, 0, charset_size);
00322 
00323         /* write x frames to buf */
00324         for (frame = 0; frame < c->mc_lifetime; frame++) {
00325             /* copy charmap to buf. buf is uchar*, charmap is int*, so no memcpy here, sorry */
00326             for (y = 0; y < b_height; y++) {
00327                 for (x = 0; x < b_width; x++) {
00328                     buf[y * b_width + x] = charmap[y * b_width + x];
00329                 }
00330             }
00331             /* advance pointers */
00332             buf += screen_size;
00333             req_size += screen_size;
00334 
00335             /* compress and copy colram to buf */
00336             if (c->mc_use_5col) {
00337                 a64_compress_colram(buf, charmap, colram);
00338                 /* advance pointers */
00339                 buf += colram_size;
00340                 req_size += colram_size;
00341             }
00342 
00343             /* advance to next charmap */
00344             charmap += 1000;
00345         }
00346 
00347         AV_WB32(avctx->extradata + 4,  c->mc_frame_counter);
00348         AV_WB32(avctx->extradata + 8,  charset_size);
00349         AV_WB32(avctx->extradata + 12, screen_size + colram_size);
00350 
00351         /* reset counter */
00352         c->mc_frame_counter = 0;
00353 
00354         if (req_size > buf_size) {
00355             av_log(avctx, AV_LOG_ERROR, "buf size too small (need %d, got %d)\n", req_size, buf_size);
00356             return -1;
00357         }
00358         return req_size;
00359     }
00360     return 0;
00361 }
00362 
00363 AVCodec ff_a64multi_encoder = {
00364     .name           = "a64multi",
00365     .type           = AVMEDIA_TYPE_VIDEO,
00366     .id             = CODEC_ID_A64_MULTI,
00367     .priv_data_size = sizeof(A64Context),
00368     .init           = a64multi_init_encoder,
00369     .encode         = a64multi_encode_frame,
00370     .close          = a64multi_close_encoder,
00371     .pix_fmts       = (const enum PixelFormat[]) {PIX_FMT_GRAY8, PIX_FMT_NONE},
00372     .long_name      = NULL_IF_CONFIG_SMALL("Multicolor charset for Commodore 64"),
00373     .capabilities   = CODEC_CAP_DELAY,
00374 };
00375 
00376 AVCodec ff_a64multi5_encoder = {
00377     .name           = "a64multi5",
00378     .type           = AVMEDIA_TYPE_VIDEO,
00379     .id             = CODEC_ID_A64_MULTI5,
00380     .priv_data_size = sizeof(A64Context),
00381     .init           = a64multi_init_encoder,
00382     .encode         = a64multi_encode_frame,
00383     .close          = a64multi_close_encoder,
00384     .pix_fmts       = (const enum PixelFormat[]) {PIX_FMT_GRAY8, PIX_FMT_NONE},
00385     .long_name      = NULL_IF_CONFIG_SMALL("Multicolor charset for Commodore 64, extended with 5th color (colram)"),
00386     .capabilities   = CODEC_CAP_DELAY,
00387 };
Generated on Thu Jan 24 2013 17:08:49 for Libav by doxygen 1.7.1