"Fossies" - the Fresh Open Source Software Archive

Member "ffmpeg-3.4.2/libavcodec/a64multienc.c" (31 Dec 2017, 14402 Bytes) of package /linux/misc/ffmpeg-3.4.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "a64multienc.c" see the Fossies "Dox" file reference documentation.

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