You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

424 lines
14KB

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