Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  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 */
  44.     AVFrame picture;
  45.  
  46.     /* variables for multicolor modes */
  47.     AVLFG randctx;
  48.     int mc_lifetime;
  49.     int mc_use_5col;
  50.     unsigned mc_frame_counter;
  51.     int *mc_meta_charset;
  52.     int *mc_charmap;
  53.     int *mc_best_cb;
  54.     int mc_luma_vals[5];
  55.     uint8_t *mc_charset;
  56.     uint8_t *mc_colram;
  57.     uint8_t *mc_palette;
  58.     int mc_pal_size;
  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.  
  189. static av_cold int a64multi_close_encoder(AVCodecContext *avctx)
  190. {
  191.     A64Context *c = avctx->priv_data;
  192.     av_free(c->mc_meta_charset);
  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.  
  200. static av_cold int a64multi_init_encoder(AVCodecContext *avctx)
  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.  
  243.     avcodec_get_frame_defaults(&c->picture);
  244.     avctx->coded_frame            = &c->picture;
  245.     avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I;
  246.     avctx->coded_frame->key_frame = 1;
  247.     if (!avctx->codec_tag)
  248.          avctx->codec_tag = AV_RL32("a64m");
  249.  
  250.     c->next_pts = AV_NOPTS_VALUE;
  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.  
  270. static int a64multi_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
  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;
  319.             p->pict_type = AV_PICTURE_TYPE_I;
  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;
  387.         c->next_pts         = AV_NOPTS_VALUE;
  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.     .long_name      = NULL_IF_CONFIG_SMALL("Multicolor charset for Commodore 64"),
  400.     .type           = AVMEDIA_TYPE_VIDEO,
  401.     .id             = AV_CODEC_ID_A64_MULTI,
  402.     .priv_data_size = sizeof(A64Context),
  403.     .init           = a64multi_init_encoder,
  404.     .encode2        = a64multi_encode_frame,
  405.     .close          = a64multi_close_encoder,
  406.     .pix_fmts       = (const enum AVPixelFormat[]) {AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE},
  407.     .capabilities   = CODEC_CAP_DELAY,
  408. };
  409. #endif
  410. #if CONFIG_A64MULTI5_ENCODER
  411. AVCodec ff_a64multi5_encoder = {
  412.     .name           = "a64multi5",
  413.     .long_name      = NULL_IF_CONFIG_SMALL("Multicolor charset for Commodore 64, extended with 5th color (colram)"),
  414.     .type           = AVMEDIA_TYPE_VIDEO,
  415.     .id             = AV_CODEC_ID_A64_MULTI5,
  416.     .priv_data_size = sizeof(A64Context),
  417.     .init           = a64multi_init_encoder,
  418.     .encode2        = a64multi_encode_frame,
  419.     .close          = a64multi_close_encoder,
  420.     .pix_fmts       = (const enum AVPixelFormat[]) {AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE},
  421.     .capabilities   = CODEC_CAP_DELAY,
  422. };
  423. #endif
  424.