annotate ffmpeg/libavcodec/dvdsubenc.c @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents 6840f77b83aa
children
rev   line source
yading@10 1 /*
yading@10 2 * DVD subtitle encoding
yading@10 3 * Copyright (c) 2005 Wolfram Gloger
yading@10 4 *
yading@10 5 * This file is part of FFmpeg.
yading@10 6 *
yading@10 7 * FFmpeg is free software; you can redistribute it and/or
yading@10 8 * modify it under the terms of the GNU Lesser General Public
yading@10 9 * License as published by the Free Software Foundation; either
yading@10 10 * version 2.1 of the License, or (at your option) any later version.
yading@10 11 *
yading@10 12 * FFmpeg is distributed in the hope that it will be useful,
yading@10 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
yading@10 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
yading@10 15 * Lesser General Public License for more details.
yading@10 16 *
yading@10 17 * You should have received a copy of the GNU Lesser General Public
yading@10 18 * License along with FFmpeg; if not, write to the Free Software
yading@10 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
yading@10 20 */
yading@10 21 #include "avcodec.h"
yading@10 22 #include "bytestream.h"
yading@10 23 #include "internal.h"
yading@10 24 #include "libavutil/avassert.h"
yading@10 25 #include "libavutil/bprint.h"
yading@10 26 #include "libavutil/imgutils.h"
yading@10 27
yading@10 28 typedef struct {
yading@10 29 uint32_t global_palette[16];
yading@10 30 } DVDSubtitleContext;
yading@10 31
yading@10 32 // ncnt is the nibble counter
yading@10 33 #define PUTNIBBLE(val)\
yading@10 34 do {\
yading@10 35 if (ncnt++ & 1)\
yading@10 36 *q++ = bitbuf | ((val) & 0x0f);\
yading@10 37 else\
yading@10 38 bitbuf = (val) << 4;\
yading@10 39 } while(0)
yading@10 40
yading@10 41 static void dvd_encode_rle(uint8_t **pq,
yading@10 42 const uint8_t *bitmap, int linesize,
yading@10 43 int w, int h,
yading@10 44 const int cmap[256])
yading@10 45 {
yading@10 46 uint8_t *q;
yading@10 47 unsigned int bitbuf = 0;
yading@10 48 int ncnt;
yading@10 49 int x, y, len, color;
yading@10 50
yading@10 51 q = *pq;
yading@10 52
yading@10 53 for (y = 0; y < h; ++y) {
yading@10 54 ncnt = 0;
yading@10 55 for(x = 0; x < w; x += len) {
yading@10 56 color = bitmap[x];
yading@10 57 for (len=1; x+len < w; ++len)
yading@10 58 if (bitmap[x+len] != color)
yading@10 59 break;
yading@10 60 color = cmap[color];
yading@10 61 av_assert0(color < 4);
yading@10 62 if (len < 0x04) {
yading@10 63 PUTNIBBLE((len << 2)|color);
yading@10 64 } else if (len < 0x10) {
yading@10 65 PUTNIBBLE(len >> 2);
yading@10 66 PUTNIBBLE((len << 2)|color);
yading@10 67 } else if (len < 0x40) {
yading@10 68 PUTNIBBLE(0);
yading@10 69 PUTNIBBLE(len >> 2);
yading@10 70 PUTNIBBLE((len << 2)|color);
yading@10 71 } else if (x+len == w) {
yading@10 72 PUTNIBBLE(0);
yading@10 73 PUTNIBBLE(0);
yading@10 74 PUTNIBBLE(0);
yading@10 75 PUTNIBBLE(color);
yading@10 76 } else {
yading@10 77 if (len > 0xff)
yading@10 78 len = 0xff;
yading@10 79 PUTNIBBLE(0);
yading@10 80 PUTNIBBLE(len >> 6);
yading@10 81 PUTNIBBLE(len >> 2);
yading@10 82 PUTNIBBLE((len << 2)|color);
yading@10 83 }
yading@10 84 }
yading@10 85 /* end of line */
yading@10 86 if (ncnt & 1)
yading@10 87 PUTNIBBLE(0);
yading@10 88 bitmap += linesize;
yading@10 89 }
yading@10 90
yading@10 91 *pq = q;
yading@10 92 }
yading@10 93
yading@10 94 static int color_distance(uint32_t a, uint32_t b)
yading@10 95 {
yading@10 96 int r = 0, d, i;
yading@10 97 int alpha_a = 8, alpha_b = 8;
yading@10 98
yading@10 99 for (i = 24; i >= 0; i -= 8) {
yading@10 100 d = alpha_a * (int)((a >> i) & 0xFF) -
yading@10 101 alpha_b * (int)((b >> i) & 0xFF);
yading@10 102 r += d * d;
yading@10 103 alpha_a = a >> 28;
yading@10 104 alpha_b = b >> 28;
yading@10 105 }
yading@10 106 return r;
yading@10 107 }
yading@10 108
yading@10 109 /**
yading@10 110 * Count colors used in a rectangle, quantizing alpha and grouping by
yading@10 111 * nearest global palette entry.
yading@10 112 */
yading@10 113 static void count_colors(AVCodecContext *avctx, unsigned hits[33],
yading@10 114 const AVSubtitleRect *r)
yading@10 115 {
yading@10 116 DVDSubtitleContext *dvdc = avctx->priv_data;
yading@10 117 unsigned count[256] = { 0 };
yading@10 118 uint32_t *palette = (uint32_t *)r->pict.data[1];
yading@10 119 uint32_t color;
yading@10 120 int x, y, i, j, match, d, best_d, av_uninit(best_j);
yading@10 121 uint8_t *p = r->pict.data[0];
yading@10 122
yading@10 123 for (y = 0; y < r->h; y++) {
yading@10 124 for (x = 0; x < r->w; x++)
yading@10 125 count[*(p++)]++;
yading@10 126 p += r->pict.linesize[0] - r->w;
yading@10 127 }
yading@10 128 for (i = 0; i < 256; i++) {
yading@10 129 if (!count[i]) /* avoid useless search */
yading@10 130 continue;
yading@10 131 color = palette[i];
yading@10 132 /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
yading@10 133 match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
yading@10 134 if (match) {
yading@10 135 best_d = INT_MAX;
yading@10 136 for (j = 0; j < 16; j++) {
yading@10 137 d = color_distance(0xFF000000 | color,
yading@10 138 0xFF000000 | dvdc->global_palette[j]);
yading@10 139 if (d < best_d) {
yading@10 140 best_d = d;
yading@10 141 best_j = j;
yading@10 142 }
yading@10 143 }
yading@10 144 match += best_j;
yading@10 145 }
yading@10 146 hits[match] += count[i];
yading@10 147 }
yading@10 148 }
yading@10 149
yading@10 150 static void select_palette(AVCodecContext *avctx, int out_palette[4],
yading@10 151 int out_alpha[4], unsigned hits[33])
yading@10 152 {
yading@10 153 DVDSubtitleContext *dvdc = avctx->priv_data;
yading@10 154 int i, j, bright, mult;
yading@10 155 uint32_t color;
yading@10 156 int selected[4] = { 0 };
yading@10 157 uint32_t pseudopal[33] = { 0 };
yading@10 158 uint32_t refcolor[3] = { 0x00000000, 0xFFFFFFFF, 0xFF000000 };
yading@10 159
yading@10 160 /* Bonus for transparent: if the rectangle fits tightly the text, the
yading@10 161 background color can be quite rare, but it would be ugly without it */
yading@10 162 hits[0] *= 16;
yading@10 163 /* Bonus for bright colors */
yading@10 164 for (i = 0; i < 16; i++) {
yading@10 165 if (!(hits[1 + i] + hits[17 + i]))
yading@10 166 continue; /* skip unused colors to gain time */
yading@10 167 color = dvdc->global_palette[i];
yading@10 168 bright = 0;
yading@10 169 for (j = 0; j < 3; j++, color >>= 8)
yading@10 170 bright += (color & 0xFF) < 0x40 || (color & 0xFF) >= 0xC0;
yading@10 171 mult = 2 + FFMIN(bright, 2);
yading@10 172 hits[ 1 + i] *= mult;
yading@10 173 hits[17 + i] *= mult;
yading@10 174 }
yading@10 175
yading@10 176 /* Select four most frequent colors */
yading@10 177 for (i = 0; i < 4; i++) {
yading@10 178 for (j = 0; j < 33; j++)
yading@10 179 if (hits[j] > hits[selected[i]])
yading@10 180 selected[i] = j;
yading@10 181 hits[selected[i]] = 0;
yading@10 182 }
yading@10 183
yading@10 184 /* Order the colors like in most DVDs:
yading@10 185 0: background, 1: foreground, 2: outline */
yading@10 186 for (i = 0; i < 16; i++) {
yading@10 187 pseudopal[ 1 + i] = 0x80000000 | dvdc->global_palette[i];
yading@10 188 pseudopal[17 + i] = 0xFF000000 | dvdc->global_palette[i];
yading@10 189 }
yading@10 190 for (i = 0; i < 3; i++) {
yading@10 191 int best_d = color_distance(refcolor[i], pseudopal[selected[i]]);
yading@10 192 for (j = i + 1; j < 4; j++) {
yading@10 193 int d = color_distance(refcolor[i], pseudopal[selected[j]]);
yading@10 194 if (d < best_d) {
yading@10 195 FFSWAP(int, selected[i], selected[j]);
yading@10 196 best_d = d;
yading@10 197 }
yading@10 198 }
yading@10 199 }
yading@10 200
yading@10 201 /* Output */
yading@10 202 for (i = 0; i < 4; i++) {
yading@10 203 out_palette[i] = selected[i] ? (selected[i] - 1) & 0xF : 0;
yading@10 204 out_alpha [i] = !selected[i] ? 0 : selected[i] < 17 ? 0x80 : 0xFF;
yading@10 205 }
yading@10 206 }
yading@10 207
yading@10 208 static void build_color_map(AVCodecContext *avctx, int cmap[],
yading@10 209 const uint32_t palette[],
yading@10 210 const int out_palette[], unsigned int const out_alpha[])
yading@10 211 {
yading@10 212 DVDSubtitleContext *dvdc = avctx->priv_data;
yading@10 213 int i, j, d, best_d;
yading@10 214 uint32_t pseudopal[4];
yading@10 215
yading@10 216 for (i = 0; i < 4; i++)
yading@10 217 pseudopal[i] = (out_alpha[i] << 24) |
yading@10 218 dvdc->global_palette[out_palette[i]];
yading@10 219 for (i = 0; i < 256; i++) {
yading@10 220 best_d = INT_MAX;
yading@10 221 for (j = 0; j < 4; j++) {
yading@10 222 d = color_distance(pseudopal[j], palette[i]);
yading@10 223 if (d < best_d) {
yading@10 224 cmap[i] = j;
yading@10 225 best_d = d;
yading@10 226 }
yading@10 227 }
yading@10 228 }
yading@10 229 }
yading@10 230
yading@10 231 static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
yading@10 232 {
yading@10 233 int x, y;
yading@10 234 uint8_t *p, *q;
yading@10 235
yading@10 236 p = src->pict.data[0];
yading@10 237 q = dst->pict.data[0] + (src->x - dst->x) +
yading@10 238 (src->y - dst->y) * dst->pict.linesize[0];
yading@10 239 for (y = 0; y < src->h; y++) {
yading@10 240 for (x = 0; x < src->w; x++)
yading@10 241 *(q++) = cmap[*(p++)];
yading@10 242 p += src->pict.linesize[0] - src->w;
yading@10 243 q += dst->pict.linesize[0] - src->w;
yading@10 244 }
yading@10 245 }
yading@10 246
yading@10 247 static int encode_dvd_subtitles(AVCodecContext *avctx,
yading@10 248 uint8_t *outbuf, int outbuf_size,
yading@10 249 const AVSubtitle *h)
yading@10 250 {
yading@10 251 DVDSubtitleContext *dvdc = avctx->priv_data;
yading@10 252 uint8_t *q, *qq;
yading@10 253 int offset1, offset2;
yading@10 254 int i, rects = h->num_rects, ret;
yading@10 255 unsigned global_palette_hits[33] = { 0 };
yading@10 256 int cmap[256];
yading@10 257 int out_palette[4];
yading@10 258 int out_alpha[4];
yading@10 259 AVSubtitleRect vrect;
yading@10 260 uint8_t *vrect_data = NULL;
yading@10 261 int x2, y2;
yading@10 262
yading@10 263 if (rects == 0 || h->rects == NULL)
yading@10 264 return AVERROR(EINVAL);
yading@10 265 for (i = 0; i < rects; i++)
yading@10 266 if (h->rects[i]->type != SUBTITLE_BITMAP) {
yading@10 267 av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
yading@10 268 return AVERROR(EINVAL);
yading@10 269 }
yading@10 270 vrect = *h->rects[0];
yading@10 271
yading@10 272 if (rects > 1) {
yading@10 273 /* DVD subtitles can have only one rectangle: build a virtual
yading@10 274 rectangle containing all actual rectangles.
yading@10 275 The data of the rectangles will be copied later, when the palette
yading@10 276 is decided, because the rectangles may have different palettes. */
yading@10 277 int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
yading@10 278 int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
yading@10 279 for (i = 1; i < rects; i++) {
yading@10 280 xmin = FFMIN(xmin, h->rects[i]->x);
yading@10 281 ymin = FFMIN(ymin, h->rects[i]->y);
yading@10 282 xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
yading@10 283 ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
yading@10 284 }
yading@10 285 vrect.x = xmin;
yading@10 286 vrect.y = ymin;
yading@10 287 vrect.w = xmax - xmin;
yading@10 288 vrect.h = ymax - ymin;
yading@10 289 if ((ret = av_image_check_size(vrect.w, vrect.h, 0, avctx)) < 0)
yading@10 290 return ret;
yading@10 291
yading@10 292 /* Count pixels outside the virtual rectangle as transparent */
yading@10 293 global_palette_hits[0] = vrect.w * vrect.h;
yading@10 294 for (i = 0; i < rects; i++)
yading@10 295 global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
yading@10 296 }
yading@10 297
yading@10 298 for (i = 0; i < rects; i++)
yading@10 299 count_colors(avctx, global_palette_hits, h->rects[i]);
yading@10 300 select_palette(avctx, out_palette, out_alpha, global_palette_hits);
yading@10 301
yading@10 302 if (rects > 1) {
yading@10 303 if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
yading@10 304 return AVERROR(ENOMEM);
yading@10 305 vrect.pict.data [0] = vrect_data;
yading@10 306 vrect.pict.linesize[0] = vrect.w;
yading@10 307 for (i = 0; i < rects; i++) {
yading@10 308 build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->pict.data[1],
yading@10 309 out_palette, out_alpha);
yading@10 310 copy_rectangle(&vrect, h->rects[i], cmap);
yading@10 311 }
yading@10 312 for (i = 0; i < 4; i++)
yading@10 313 cmap[i] = i;
yading@10 314 } else {
yading@10 315 build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->pict.data[1],
yading@10 316 out_palette, out_alpha);
yading@10 317 }
yading@10 318
yading@10 319 av_log(avctx, AV_LOG_DEBUG, "Selected palette:");
yading@10 320 for (i = 0; i < 4; i++)
yading@10 321 av_log(avctx, AV_LOG_DEBUG, " 0x%06x@@%02x (0x%x,0x%x)",
yading@10 322 dvdc->global_palette[out_palette[i]], out_alpha[i],
yading@10 323 out_palette[i], out_alpha[i] >> 4);
yading@10 324 av_log(avctx, AV_LOG_DEBUG, "\n");
yading@10 325
yading@10 326 // encode data block
yading@10 327 q = outbuf + 4;
yading@10 328 offset1 = q - outbuf;
yading@10 329 // worst case memory requirement: 1 nibble per pixel..
yading@10 330 if ((q - outbuf) + vrect.w * vrect.h / 2 + 17 + 21 > outbuf_size) {
yading@10 331 av_log(NULL, AV_LOG_ERROR, "dvd_subtitle too big\n");
yading@10 332 ret = AVERROR_BUFFER_TOO_SMALL;
yading@10 333 goto fail;
yading@10 334 }
yading@10 335 dvd_encode_rle(&q, vrect.pict.data[0], vrect.w * 2,
yading@10 336 vrect.w, (vrect.h + 1) >> 1, cmap);
yading@10 337 offset2 = q - outbuf;
yading@10 338 dvd_encode_rle(&q, vrect.pict.data[0] + vrect.w, vrect.w * 2,
yading@10 339 vrect.w, vrect.h >> 1, cmap);
yading@10 340
yading@10 341 // set data packet size
yading@10 342 qq = outbuf + 2;
yading@10 343 bytestream_put_be16(&qq, q - outbuf);
yading@10 344
yading@10 345 // send start display command
yading@10 346 bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
yading@10 347 bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2);
yading@10 348 *q++ = 0x03; // palette - 4 nibbles
yading@10 349 *q++ = (out_palette[3] << 4) | out_palette[2];
yading@10 350 *q++ = (out_palette[1] << 4) | out_palette[0];
yading@10 351 *q++ = 0x04; // alpha - 4 nibbles
yading@10 352 *q++ = (out_alpha[3] & 0xF0) | (out_alpha[2] >> 4);
yading@10 353 *q++ = (out_alpha[1] & 0xF0) | (out_alpha[0] >> 4);
yading@10 354
yading@10 355 // 12 bytes per rect
yading@10 356 x2 = vrect.x + vrect.w - 1;
yading@10 357 y2 = vrect.y + vrect.h - 1;
yading@10 358
yading@10 359 *q++ = 0x05;
yading@10 360 // x1 x2 -> 6 nibbles
yading@10 361 *q++ = vrect.x >> 4;
yading@10 362 *q++ = (vrect.x << 4) | ((x2 >> 8) & 0xf);
yading@10 363 *q++ = x2;
yading@10 364 // y1 y2 -> 6 nibbles
yading@10 365 *q++ = vrect.y >> 4;
yading@10 366 *q++ = (vrect.y << 4) | ((y2 >> 8) & 0xf);
yading@10 367 *q++ = y2;
yading@10 368
yading@10 369 *q++ = 0x06;
yading@10 370 // offset1, offset2
yading@10 371 bytestream_put_be16(&q, offset1);
yading@10 372 bytestream_put_be16(&q, offset2);
yading@10 373
yading@10 374 *q++ = 0x01; // start command
yading@10 375 *q++ = 0xff; // terminating command
yading@10 376
yading@10 377 // send stop display command last
yading@10 378 bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
yading@10 379 bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
yading@10 380 *q++ = 0x02; // set end
yading@10 381 *q++ = 0xff; // terminating command
yading@10 382
yading@10 383 qq = outbuf;
yading@10 384 bytestream_put_be16(&qq, q - outbuf);
yading@10 385
yading@10 386 av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%td\n", q - outbuf);
yading@10 387 ret = q - outbuf;
yading@10 388
yading@10 389 fail:
yading@10 390 av_free(vrect_data);
yading@10 391 return ret;
yading@10 392 }
yading@10 393
yading@10 394 static int dvdsub_init(AVCodecContext *avctx)
yading@10 395 {
yading@10 396 DVDSubtitleContext *dvdc = avctx->priv_data;
yading@10 397 static const uint32_t default_palette[16] = {
yading@10 398 0x000000, 0x0000FF, 0x00FF00, 0xFF0000,
yading@10 399 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFFFFF,
yading@10 400 0x808000, 0x8080FF, 0x800080, 0x80FF80,
yading@10 401 0x008080, 0xFF8080, 0x555555, 0xAAAAAA,
yading@10 402 };
yading@10 403 AVBPrint extradata;
yading@10 404 int i, ret;
yading@10 405
yading@10 406 av_assert0(sizeof(dvdc->global_palette) == sizeof(default_palette));
yading@10 407 memcpy(dvdc->global_palette, default_palette, sizeof(dvdc->global_palette));
yading@10 408
yading@10 409 av_bprint_init(&extradata, 0, 1);
yading@10 410 if (avctx->width && avctx->height)
yading@10 411 av_bprintf(&extradata, "size: %dx%d\n", avctx->width, avctx->height);
yading@10 412 av_bprintf(&extradata, "palette:");
yading@10 413 for (i = 0; i < 16; i++)
yading@10 414 av_bprintf(&extradata, " %06"PRIx32"%c",
yading@10 415 dvdc->global_palette[i] & 0xFFFFFF, i < 15 ? ',' : '\n');
yading@10 416
yading@10 417 ret = avpriv_bprint_to_extradata(avctx, &extradata);
yading@10 418 if (ret < 0)
yading@10 419 return ret;
yading@10 420
yading@10 421 return 0;
yading@10 422 }
yading@10 423
yading@10 424 static int dvdsub_encode(AVCodecContext *avctx,
yading@10 425 unsigned char *buf, int buf_size,
yading@10 426 const AVSubtitle *sub)
yading@10 427 {
yading@10 428 //DVDSubtitleContext *s = avctx->priv_data;
yading@10 429 int ret;
yading@10 430
yading@10 431 ret = encode_dvd_subtitles(avctx, buf, buf_size, sub);
yading@10 432 return ret;
yading@10 433 }
yading@10 434
yading@10 435 AVCodec ff_dvdsub_encoder = {
yading@10 436 .name = "dvdsub",
yading@10 437 .type = AVMEDIA_TYPE_SUBTITLE,
yading@10 438 .id = AV_CODEC_ID_DVD_SUBTITLE,
yading@10 439 .init = dvdsub_init,
yading@10 440 .encode_sub = dvdsub_encode,
yading@10 441 .long_name = NULL_IF_CONFIG_SMALL("DVD subtitles"),
yading@10 442 .priv_data_size = sizeof(DVDSubtitleContext),
yading@10 443 };