Subversion Repositories Kolibri OS

Rev

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/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
  426.