1/* 2 * DVD subtitle encoding 3 * Copyright (c) 2005 Wolfram Gloger 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#include "avcodec.h" 22#include "bytestream.h" 23#include "codec_internal.h" 24#include "internal.h" 25#include "libavutil/avassert.h" 26#include "libavutil/bprint.h" 27#include "libavutil/imgutils.h" 28#include "libavutil/opt.h" 29 30typedef struct { 31 AVClass *class; 32 uint32_t global_palette[16]; 33 char *palette_str; 34 int even_rows_fix; 35} DVDSubtitleContext; 36 37// ncnt is the nibble counter 38#define PUTNIBBLE(val)\ 39do {\ 40 if (ncnt++ & 1)\ 41 *q++ = bitbuf | ((val) & 0x0f);\ 42 else\ 43 bitbuf = (val) << 4;\ 44} while(0) 45 46static void dvd_encode_rle(uint8_t **pq, 47 const uint8_t *bitmap, int linesize, 48 int w, int h, 49 const int cmap[256]) 50{ 51 uint8_t *q; 52 unsigned int bitbuf = 0; 53 int ncnt; 54 int x, y, len, color; 55 56 q = *pq; 57 58 for (y = 0; y < h; ++y) { 59 ncnt = 0; 60 for(x = 0; x < w; x += len) { 61 color = bitmap[x]; 62 for (len=1; x+len < w; ++len) 63 if (bitmap[x+len] != color) 64 break; 65 color = cmap[color]; 66 av_assert0(color < 4); 67 if (len < 0x04) { 68 PUTNIBBLE((len << 2)|color); 69 } else if (len < 0x10) { 70 PUTNIBBLE(len >> 2); 71 PUTNIBBLE((len << 2)|color); 72 } else if (len < 0x40) { 73 PUTNIBBLE(0); 74 PUTNIBBLE(len >> 2); 75 PUTNIBBLE((len << 2)|color); 76 } else if (x+len == w) { 77 PUTNIBBLE(0); 78 PUTNIBBLE(0); 79 PUTNIBBLE(0); 80 PUTNIBBLE(color); 81 } else { 82 if (len > 0xff) 83 len = 0xff; 84 PUTNIBBLE(0); 85 PUTNIBBLE(len >> 6); 86 PUTNIBBLE(len >> 2); 87 PUTNIBBLE((len << 2)|color); 88 } 89 } 90 /* end of line */ 91 if (ncnt & 1) 92 PUTNIBBLE(0); 93 bitmap += linesize; 94 } 95 96 *pq = q; 97} 98 99static int color_distance(uint32_t a, uint32_t b) 100{ 101 int r = 0, d, i; 102 int alpha_a = 8, alpha_b = 8; 103 104 for (i = 24; i >= 0; i -= 8) { 105 d = alpha_a * (int)((a >> i) & 0xFF) - 106 alpha_b * (int)((b >> i) & 0xFF); 107 r += d * d; 108 alpha_a = a >> 28; 109 alpha_b = b >> 28; 110 } 111 return r; 112} 113 114/** 115 * Count colors used in a rectangle, quantizing alpha and grouping by 116 * nearest global palette entry. 117 */ 118static void count_colors(AVCodecContext *avctx, unsigned hits[33], 119 const AVSubtitleRect *r) 120{ 121 DVDSubtitleContext *dvdc = avctx->priv_data; 122 unsigned count[256] = { 0 }; 123 uint32_t *palette = (uint32_t *)r->data[1]; 124 uint32_t color; 125 int x, y, i, j, match, d, best_d, av_uninit(best_j); 126 uint8_t *p = r->data[0]; 127 128 for (y = 0; y < r->h; y++) { 129 for (x = 0; x < r->w; x++) 130 count[*(p++)]++; 131 p += r->linesize[0] - r->w; 132 } 133 for (i = 0; i < 256; i++) { 134 if (!count[i]) /* avoid useless search */ 135 continue; 136 color = palette[i]; 137 /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */ 138 match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17; 139 if (match) { 140 best_d = INT_MAX; 141 for (j = 0; j < 16; j++) { 142 d = color_distance(0xFF000000 | color, 143 0xFF000000 | dvdc->global_palette[j]); 144 if (d < best_d) { 145 best_d = d; 146 best_j = j; 147 } 148 } 149 match += best_j; 150 } 151 hits[match] += count[i]; 152 } 153} 154 155static void select_palette(AVCodecContext *avctx, int out_palette[4], 156 int out_alpha[4], unsigned hits[33]) 157{ 158 DVDSubtitleContext *dvdc = avctx->priv_data; 159 int i, j, bright, mult; 160 uint32_t color; 161 int selected[4] = { 0 }; 162 uint32_t pseudopal[33] = { 0 }; 163 uint32_t refcolor[3] = { 0x00000000, 0xFFFFFFFF, 0xFF000000 }; 164 165 /* Bonus for transparent: if the rectangle fits tightly the text, the 166 background color can be quite rare, but it would be ugly without it */ 167 hits[0] *= 16; 168 /* Bonus for bright colors */ 169 for (i = 0; i < 16; i++) { 170 if (!(hits[1 + i] + hits[17 + i])) 171 continue; /* skip unused colors to gain time */ 172 color = dvdc->global_palette[i]; 173 bright = 0; 174 for (j = 0; j < 3; j++, color >>= 8) 175 bright += (color & 0xFF) < 0x40 || (color & 0xFF) >= 0xC0; 176 mult = 2 + FFMIN(bright, 2); 177 hits[ 1 + i] *= mult; 178 hits[17 + i] *= mult; 179 } 180 181 /* Select four most frequent colors */ 182 for (i = 0; i < 4; i++) { 183 for (j = 0; j < 33; j++) 184 if (hits[j] > hits[selected[i]]) 185 selected[i] = j; 186 hits[selected[i]] = 0; 187 } 188 189 /* Order the colors like in most DVDs: 190 0: background, 1: foreground, 2: outline */ 191 for (i = 0; i < 16; i++) { 192 pseudopal[ 1 + i] = 0x80000000 | dvdc->global_palette[i]; 193 pseudopal[17 + i] = 0xFF000000 | dvdc->global_palette[i]; 194 } 195 for (i = 0; i < 3; i++) { 196 int best_d = color_distance(refcolor[i], pseudopal[selected[i]]); 197 for (j = i + 1; j < 4; j++) { 198 int d = color_distance(refcolor[i], pseudopal[selected[j]]); 199 if (d < best_d) { 200 FFSWAP(int, selected[i], selected[j]); 201 best_d = d; 202 } 203 } 204 } 205 206 /* Output */ 207 for (i = 0; i < 4; i++) { 208 out_palette[i] = selected[i] ? (selected[i] - 1) & 0xF : 0; 209 out_alpha [i] = !selected[i] ? 0 : selected[i] < 17 ? 0x80 : 0xFF; 210 } 211} 212 213static void build_color_map(AVCodecContext *avctx, int cmap[], 214 const uint32_t palette[], 215 const int out_palette[], unsigned int const out_alpha[]) 216{ 217 DVDSubtitleContext *dvdc = avctx->priv_data; 218 int i, j, d, best_d; 219 uint32_t pseudopal[4]; 220 221 for (i = 0; i < 4; i++) 222 pseudopal[i] = (out_alpha[i] << 24) | 223 dvdc->global_palette[out_palette[i]]; 224 for (i = 0; i < 256; i++) { 225 best_d = INT_MAX; 226 for (j = 0; j < 4; j++) { 227 d = color_distance(pseudopal[j], palette[i]); 228 if (d < best_d) { 229 cmap[i] = j; 230 best_d = d; 231 } 232 } 233 } 234} 235 236static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[]) 237{ 238 int x, y; 239 uint8_t *p, *q; 240 241 p = src->data[0]; 242 q = dst->data[0] + (src->x - dst->x) + 243 (src->y - dst->y) * dst->linesize[0]; 244 for (y = 0; y < src->h; y++) { 245 for (x = 0; x < src->w; x++) 246 *(q++) = cmap[*(p++)]; 247 p += src->linesize[0] - src->w; 248 q += dst->linesize[0] - src->w; 249 } 250} 251 252static int encode_dvd_subtitles(AVCodecContext *avctx, 253 uint8_t *outbuf, int outbuf_size, 254 const AVSubtitle *h) 255{ 256 DVDSubtitleContext *dvdc = avctx->priv_data; 257 uint8_t *q, *qq; 258 int offset1, offset2; 259 int i, rects = h->num_rects, ret; 260 unsigned global_palette_hits[33] = { 0 }; 261 int cmap[256]; 262 int out_palette[4]; 263 int out_alpha[4]; 264 AVSubtitleRect vrect; 265 uint8_t *vrect_data = NULL; 266 int x2, y2; 267 int forced = 0; 268 269 if (rects == 0 || !h->rects) 270 return AVERROR(EINVAL); 271 for (i = 0; i < rects; i++) 272 if (h->rects[i]->type != SUBTITLE_BITMAP) { 273 av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n"); 274 return AVERROR(EINVAL); 275 } 276 /* Mark this subtitle forced if any of the rectangles is forced. */ 277 for (i = 0; i < rects; i++) 278 if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) { 279 forced = 1; 280 break; 281 } 282 283 vrect = *h->rects[0]; 284 285 if (rects > 1) { 286 /* DVD subtitles can have only one rectangle: build a virtual 287 rectangle containing all actual rectangles. 288 The data of the rectangles will be copied later, when the palette 289 is decided, because the rectangles may have different palettes. */ 290 int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w; 291 int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h; 292 for (i = 1; i < rects; i++) { 293 xmin = FFMIN(xmin, h->rects[i]->x); 294 ymin = FFMIN(ymin, h->rects[i]->y); 295 xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w); 296 ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h); 297 } 298 vrect.x = xmin; 299 vrect.y = ymin; 300 vrect.w = xmax - xmin; 301 vrect.h = ymax - ymin; 302 if ((ret = av_image_check_size(vrect.w, vrect.h, 0, avctx)) < 0) 303 return ret; 304 305 /* Count pixels outside the virtual rectangle as transparent */ 306 global_palette_hits[0] = vrect.w * vrect.h; 307 for (i = 0; i < rects; i++) 308 global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h; 309 } 310 311 for (i = 0; i < rects; i++) 312 count_colors(avctx, global_palette_hits, h->rects[i]); 313 select_palette(avctx, out_palette, out_alpha, global_palette_hits); 314 315 if (rects > 1) { 316 if (!(vrect_data = av_calloc(vrect.w, vrect.h))) 317 return AVERROR(ENOMEM); 318 vrect.data [0] = vrect_data; 319 vrect.linesize[0] = vrect.w; 320 for (i = 0; i < rects; i++) { 321 build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1], 322 out_palette, out_alpha); 323 copy_rectangle(&vrect, h->rects[i], cmap); 324 } 325 for (i = 0; i < 4; i++) 326 cmap[i] = i; 327 } else { 328 build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1], 329 out_palette, out_alpha); 330 } 331 332 av_log(avctx, AV_LOG_DEBUG, "Selected palette:"); 333 for (i = 0; i < 4; i++) 334 av_log(avctx, AV_LOG_DEBUG, " 0x%06"PRIx32"@@%02x (0x%x,0x%x)", 335 dvdc->global_palette[out_palette[i]], out_alpha[i], 336 out_palette[i], out_alpha[i] >> 4); 337 av_log(avctx, AV_LOG_DEBUG, "\n"); 338 339 // encode data block 340 q = outbuf + 4; 341 offset1 = q - outbuf; 342 // worst case memory requirement: 1 nibble per pixel.. 343 if ((q - outbuf) + vrect.w * vrect.h / 2 + 17 + 21 > outbuf_size) { 344 av_log(NULL, AV_LOG_ERROR, "dvd_subtitle too big\n"); 345 ret = AVERROR_BUFFER_TOO_SMALL; 346 goto fail; 347 } 348 dvd_encode_rle(&q, vrect.data[0], vrect.w * 2, 349 vrect.w, (vrect.h + 1) >> 1, cmap); 350 offset2 = q - outbuf; 351 dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2, 352 vrect.w, vrect.h >> 1, cmap); 353 354 if (dvdc->even_rows_fix && (vrect.h & 1)) { 355 // Work-around for some players that want the height to be even. 356 vrect.h++; 357 *q++ = 0x00; // 0x00 0x00 == empty row, i.e. fully transparent 358 *q++ = 0x00; 359 } 360 361 // set data packet size 362 qq = outbuf + 2; 363 bytestream_put_be16(&qq, q - outbuf); 364 365 // send start display command 366 bytestream_put_be16(&q, (h->start_display_time*90) >> 10); 367 bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2); 368 *q++ = 0x03; // palette - 4 nibbles 369 *q++ = (out_palette[3] << 4) | out_palette[2]; 370 *q++ = (out_palette[1] << 4) | out_palette[0]; 371 *q++ = 0x04; // alpha - 4 nibbles 372 *q++ = (out_alpha[3] & 0xF0) | (out_alpha[2] >> 4); 373 *q++ = (out_alpha[1] & 0xF0) | (out_alpha[0] >> 4); 374 375 // 12 bytes per rect 376 x2 = vrect.x + vrect.w - 1; 377 y2 = vrect.y + vrect.h - 1; 378 379 if (x2 > avctx->width || y2 > avctx->height) { 380 av_log(avctx, AV_LOG_ERROR, "canvas_size(%d:%d) is too small(%d:%d) for render\n", 381 avctx->width, avctx->height, x2, y2); 382 ret = AVERROR(EINVAL);; 383 goto fail; 384 } 385 *q++ = 0x05; 386 // x1 x2 -> 6 nibbles 387 *q++ = vrect.x >> 4; 388 *q++ = (vrect.x << 4) | ((x2 >> 8) & 0xf); 389 *q++ = x2; 390 // y1 y2 -> 6 nibbles 391 *q++ = vrect.y >> 4; 392 *q++ = (vrect.y << 4) | ((y2 >> 8) & 0xf); 393 *q++ = y2; 394 395 *q++ = 0x06; 396 // offset1, offset2 397 bytestream_put_be16(&q, offset1); 398 bytestream_put_be16(&q, offset2); 399 400 *q++ = forced ? 0x00 : 0x01; // start command 401 *q++ = 0xff; // terminating command 402 403 // send stop display command last 404 bytestream_put_be16(&q, (h->end_display_time*90) >> 10); 405 bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/); 406 *q++ = 0x02; // set end 407 *q++ = 0xff; // terminating command 408 409 qq = outbuf; 410 bytestream_put_be16(&qq, q - outbuf); 411 412 av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf); 413 ret = q - outbuf; 414 415fail: 416 av_free(vrect_data); 417 return ret; 418} 419 420static int bprint_to_extradata(AVCodecContext *avctx, struct AVBPrint *buf) 421{ 422 int ret; 423 char *str; 424 425 ret = av_bprint_finalize(buf, &str); 426 if (ret < 0) 427 return ret; 428 if (!av_bprint_is_complete(buf)) { 429 av_free(str); 430 return AVERROR(ENOMEM); 431 } 432 433 avctx->extradata = str; 434 /* Note: the string is NUL terminated (so extradata can be read as a 435 * string), but the ending character is not accounted in the size (in 436 * binary formats you are likely not supposed to mux that character). When 437 * extradata is copied, it is also padded with AV_INPUT_BUFFER_PADDING_SIZE 438 * zeros. */ 439 avctx->extradata_size = buf->len; 440 return 0; 441} 442 443static int dvdsub_init(AVCodecContext *avctx) 444{ 445 DVDSubtitleContext *dvdc = avctx->priv_data; 446 static const uint32_t default_palette[16] = { 447 0x000000, 0x0000FF, 0x00FF00, 0xFF0000, 448 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFFFFF, 449 0x808000, 0x8080FF, 0x800080, 0x80FF80, 450 0x008080, 0xFF8080, 0x555555, 0xAAAAAA, 451 }; 452 AVBPrint extradata; 453 int i, ret; 454 455 av_assert0(sizeof(dvdc->global_palette) == sizeof(default_palette)); 456 if (dvdc->palette_str) { 457 ff_dvdsub_parse_palette(dvdc->global_palette, dvdc->palette_str); 458 } else { 459 memcpy(dvdc->global_palette, default_palette, sizeof(dvdc->global_palette)); 460 } 461 462 av_bprint_init(&extradata, 0, AV_BPRINT_SIZE_AUTOMATIC); 463 if (avctx->width && avctx->height) 464 av_bprintf(&extradata, "size: %dx%d\n", avctx->width, avctx->height); 465 av_bprintf(&extradata, "palette:"); 466 for (i = 0; i < 16; i++) 467 av_bprintf(&extradata, " %06"PRIx32"%c", 468 dvdc->global_palette[i] & 0xFFFFFF, i < 15 ? ',' : '\n'); 469 470 ret = bprint_to_extradata(avctx, &extradata); 471 if (ret < 0) 472 return ret; 473 474 return 0; 475} 476 477static int dvdsub_encode(AVCodecContext *avctx, 478 unsigned char *buf, int buf_size, 479 const AVSubtitle *sub) 480{ 481 //DVDSubtitleContext *s = avctx->priv_data; 482 int ret; 483 484 ret = encode_dvd_subtitles(avctx, buf, buf_size, sub); 485 return ret; 486} 487 488#define OFFSET(x) offsetof(DVDSubtitleContext, x) 489#define SE AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_ENCODING_PARAM 490static const AVOption options[] = { 491 {"palette", "set the global palette", OFFSET(palette_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SE }, 492 {"even_rows_fix", "Make number of rows even (workaround for some players)", OFFSET(even_rows_fix), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, SE}, 493 { NULL }, 494}; 495 496static const AVClass dvdsubenc_class = { 497 .class_name = "VOBSUB subtitle encoder", 498 .item_name = av_default_item_name, 499 .option = options, 500 .version = LIBAVUTIL_VERSION_INT, 501}; 502 503const FFCodec ff_dvdsub_encoder = { 504 .p.name = "dvdsub", 505 .p.long_name = NULL_IF_CONFIG_SMALL("DVD subtitles"), 506 .p.type = AVMEDIA_TYPE_SUBTITLE, 507 .p.id = AV_CODEC_ID_DVD_SUBTITLE, 508 .init = dvdsub_init, 509 FF_CODEC_ENCODE_SUB_CB(dvdsub_encode), 510 .p.priv_class = &dvdsubenc_class, 511 .priv_data_size = sizeof(DVDSubtitleContext), 512 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 513}; 514