1/* 2 * Closed Caption Decoding 3 * Copyright (c) 2015 Anshul Maheshwari 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#include "avcodec.h" 23#include "ass.h" 24#include "codec_internal.h" 25#include "libavutil/opt.h" 26 27#define SCREEN_ROWS 15 28#define SCREEN_COLUMNS 32 29 30#define SET_FLAG(var, val) ( (var) |= ( 1 << (val)) ) 31#define UNSET_FLAG(var, val) ( (var) &= ~( 1 << (val)) ) 32#define CHECK_FLAG(var, val) ( (var) & ( 1 << (val)) ) 33 34static const AVRational ms_tb = {1, 1000}; 35 36enum cc_mode { 37 CCMODE_POPON, 38 CCMODE_PAINTON, 39 CCMODE_ROLLUP, 40 CCMODE_TEXT, 41}; 42 43enum cc_color_code { 44 CCCOL_WHITE, 45 CCCOL_GREEN, 46 CCCOL_BLUE, 47 CCCOL_CYAN, 48 CCCOL_RED, 49 CCCOL_YELLOW, 50 CCCOL_MAGENTA, 51 CCCOL_USERDEFINED, 52 CCCOL_BLACK, 53 CCCOL_TRANSPARENT, 54}; 55 56enum cc_font { 57 CCFONT_REGULAR, 58 CCFONT_ITALICS, 59 CCFONT_UNDERLINED, 60 CCFONT_UNDERLINED_ITALICS, 61}; 62 63enum cc_charset { 64 CCSET_BASIC_AMERICAN, 65 CCSET_SPECIAL_AMERICAN, 66 CCSET_EXTENDED_SPANISH_FRENCH_MISC, 67 CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH, 68}; 69 70static const char *charset_overrides[4][128] = 71{ 72 [CCSET_BASIC_AMERICAN] = { 73 [0x27] = "\u2019", 74 [0x2a] = "\u00e1", 75 [0x5c] = "\u00e9", 76 [0x5e] = "\u00ed", 77 [0x5f] = "\u00f3", 78 [0x60] = "\u00fa", 79 [0x7b] = "\u00e7", 80 [0x7c] = "\u00f7", 81 [0x7d] = "\u00d1", 82 [0x7e] = "\u00f1", 83 [0x7f] = "\u2588" 84 }, 85 [CCSET_SPECIAL_AMERICAN] = { 86 [0x30] = "\u00ae", 87 [0x31] = "\u00b0", 88 [0x32] = "\u00bd", 89 [0x33] = "\u00bf", 90 [0x34] = "\u2122", 91 [0x35] = "\u00a2", 92 [0x36] = "\u00a3", 93 [0x37] = "\u266a", 94 [0x38] = "\u00e0", 95 [0x39] = "\u00A0", 96 [0x3a] = "\u00e8", 97 [0x3b] = "\u00e2", 98 [0x3c] = "\u00ea", 99 [0x3d] = "\u00ee", 100 [0x3e] = "\u00f4", 101 [0x3f] = "\u00fb", 102 }, 103 [CCSET_EXTENDED_SPANISH_FRENCH_MISC] = { 104 [0x20] = "\u00c1", 105 [0x21] = "\u00c9", 106 [0x22] = "\u00d3", 107 [0x23] = "\u00da", 108 [0x24] = "\u00dc", 109 [0x25] = "\u00fc", 110 [0x26] = "\u00b4", 111 [0x27] = "\u00a1", 112 [0x28] = "*", 113 [0x29] = "\u2018", 114 [0x2a] = "-", 115 [0x2b] = "\u00a9", 116 [0x2c] = "\u2120", 117 [0x2d] = "\u00b7", 118 [0x2e] = "\u201c", 119 [0x2f] = "\u201d", 120 [0x30] = "\u00c0", 121 [0x31] = "\u00c2", 122 [0x32] = "\u00c7", 123 [0x33] = "\u00c8", 124 [0x34] = "\u00ca", 125 [0x35] = "\u00cb", 126 [0x36] = "\u00eb", 127 [0x37] = "\u00ce", 128 [0x38] = "\u00cf", 129 [0x39] = "\u00ef", 130 [0x3a] = "\u00d4", 131 [0x3b] = "\u00d9", 132 [0x3c] = "\u00f9", 133 [0x3d] = "\u00db", 134 [0x3e] = "\u00ab", 135 [0x3f] = "\u00bb", 136 }, 137 [CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH] = { 138 [0x20] = "\u00c3", 139 [0x21] = "\u00e3", 140 [0x22] = "\u00cd", 141 [0x23] = "\u00cc", 142 [0x24] = "\u00ec", 143 [0x25] = "\u00d2", 144 [0x26] = "\u00f2", 145 [0x27] = "\u00d5", 146 [0x28] = "\u00f5", 147 [0x29] = "{", 148 [0x2a] = "}", 149 [0x2b] = "\\", 150 [0x2c] = "^", 151 [0x2d] = "_", 152 [0x2e] = "|", 153 [0x2f] = "~", 154 [0x30] = "\u00c4", 155 [0x31] = "\u00e4", 156 [0x32] = "\u00d6", 157 [0x33] = "\u00f6", 158 [0x34] = "\u00df", 159 [0x35] = "\u00a5", 160 [0x36] = "\u00a4", 161 [0x37] = "\u00a6", 162 [0x38] = "\u00c5", 163 [0x39] = "\u00e5", 164 [0x3a] = "\u00d8", 165 [0x3b] = "\u00f8", 166 [0x3c] = "\u250c", 167 [0x3d] = "\u2510", 168 [0x3e] = "\u2514", 169 [0x3f] = "\u2518", 170 }, 171}; 172 173static const unsigned char bg_attribs[8] = // Color 174{ 175 CCCOL_WHITE, 176 CCCOL_GREEN, 177 CCCOL_BLUE, 178 CCCOL_CYAN, 179 CCCOL_RED, 180 CCCOL_YELLOW, 181 CCCOL_MAGENTA, 182 CCCOL_BLACK, 183}; 184 185static const unsigned char pac2_attribs[32][3] = // Color, font, ident 186{ 187 { CCCOL_WHITE, CCFONT_REGULAR, 0 }, // 0x40 || 0x60 188 { CCCOL_WHITE, CCFONT_UNDERLINED, 0 }, // 0x41 || 0x61 189 { CCCOL_GREEN, CCFONT_REGULAR, 0 }, // 0x42 || 0x62 190 { CCCOL_GREEN, CCFONT_UNDERLINED, 0 }, // 0x43 || 0x63 191 { CCCOL_BLUE, CCFONT_REGULAR, 0 }, // 0x44 || 0x64 192 { CCCOL_BLUE, CCFONT_UNDERLINED, 0 }, // 0x45 || 0x65 193 { CCCOL_CYAN, CCFONT_REGULAR, 0 }, // 0x46 || 0x66 194 { CCCOL_CYAN, CCFONT_UNDERLINED, 0 }, // 0x47 || 0x67 195 { CCCOL_RED, CCFONT_REGULAR, 0 }, // 0x48 || 0x68 196 { CCCOL_RED, CCFONT_UNDERLINED, 0 }, // 0x49 || 0x69 197 { CCCOL_YELLOW, CCFONT_REGULAR, 0 }, // 0x4a || 0x6a 198 { CCCOL_YELLOW, CCFONT_UNDERLINED, 0 }, // 0x4b || 0x6b 199 { CCCOL_MAGENTA, CCFONT_REGULAR, 0 }, // 0x4c || 0x6c 200 { CCCOL_MAGENTA, CCFONT_UNDERLINED, 0 }, // 0x4d || 0x6d 201 { CCCOL_WHITE, CCFONT_ITALICS, 0 }, // 0x4e || 0x6e 202 { CCCOL_WHITE, CCFONT_UNDERLINED_ITALICS, 0 }, // 0x4f || 0x6f 203 { CCCOL_WHITE, CCFONT_REGULAR, 0 }, // 0x50 || 0x70 204 { CCCOL_WHITE, CCFONT_UNDERLINED, 0 }, // 0x51 || 0x71 205 { CCCOL_WHITE, CCFONT_REGULAR, 4 }, // 0x52 || 0x72 206 { CCCOL_WHITE, CCFONT_UNDERLINED, 4 }, // 0x53 || 0x73 207 { CCCOL_WHITE, CCFONT_REGULAR, 8 }, // 0x54 || 0x74 208 { CCCOL_WHITE, CCFONT_UNDERLINED, 8 }, // 0x55 || 0x75 209 { CCCOL_WHITE, CCFONT_REGULAR, 12 }, // 0x56 || 0x76 210 { CCCOL_WHITE, CCFONT_UNDERLINED, 12 }, // 0x57 || 0x77 211 { CCCOL_WHITE, CCFONT_REGULAR, 16 }, // 0x58 || 0x78 212 { CCCOL_WHITE, CCFONT_UNDERLINED, 16 }, // 0x59 || 0x79 213 { CCCOL_WHITE, CCFONT_REGULAR, 20 }, // 0x5a || 0x7a 214 { CCCOL_WHITE, CCFONT_UNDERLINED, 20 }, // 0x5b || 0x7b 215 { CCCOL_WHITE, CCFONT_REGULAR, 24 }, // 0x5c || 0x7c 216 { CCCOL_WHITE, CCFONT_UNDERLINED, 24 }, // 0x5d || 0x7d 217 { CCCOL_WHITE, CCFONT_REGULAR, 28 }, // 0x5e || 0x7e 218 { CCCOL_WHITE, CCFONT_UNDERLINED, 28 } // 0x5f || 0x7f 219 /* total 32 entries */ 220}; 221 222struct Screen { 223 /* +1 is used to compensate null character of string */ 224 uint8_t characters[SCREEN_ROWS+1][SCREEN_COLUMNS+1]; 225 uint8_t charsets[SCREEN_ROWS+1][SCREEN_COLUMNS+1]; 226 uint8_t colors[SCREEN_ROWS+1][SCREEN_COLUMNS+1]; 227 uint8_t bgs[SCREEN_ROWS+1][SCREEN_COLUMNS+1]; 228 uint8_t fonts[SCREEN_ROWS+1][SCREEN_COLUMNS+1]; 229 /* 230 * Bitmask of used rows; if a bit is not set, the 231 * corresponding row is not used. 232 * for setting row 1 use row | (1 << 0) 233 * for setting row 15 use row | (1 << 14) 234 */ 235 int16_t row_used; 236}; 237 238typedef struct CCaptionSubContext { 239 AVClass *class; 240 int real_time; 241 int real_time_latency_msec; 242 int data_field; 243 struct Screen screen[2]; 244 int active_screen; 245 uint8_t cursor_row; 246 uint8_t cursor_column; 247 uint8_t cursor_color; 248 uint8_t bg_color; 249 uint8_t cursor_font; 250 uint8_t cursor_charset; 251 AVBPrint buffer[2]; 252 int buffer_index; 253 int buffer_changed; 254 int rollup; 255 enum cc_mode mode; 256 int64_t buffer_time[2]; 257 int screen_touched; 258 int64_t last_real_time; 259 uint8_t prev_cmd[2]; 260 int readorder; 261} CCaptionSubContext; 262 263static av_cold int init_decoder(AVCodecContext *avctx) 264{ 265 int ret; 266 CCaptionSubContext *ctx = avctx->priv_data; 267 268 av_bprint_init(&ctx->buffer[0], 0, AV_BPRINT_SIZE_UNLIMITED); 269 av_bprint_init(&ctx->buffer[1], 0, AV_BPRINT_SIZE_UNLIMITED); 270 /* taking by default roll up to 2 */ 271 ctx->mode = CCMODE_ROLLUP; 272 ctx->bg_color = CCCOL_BLACK; 273 ctx->rollup = 2; 274 ctx->cursor_row = 10; 275 ret = ff_ass_subtitle_header(avctx, "Monospace", 276 ASS_DEFAULT_FONT_SIZE, 277 ASS_DEFAULT_COLOR, 278 ASS_DEFAULT_BACK_COLOR, 279 ASS_DEFAULT_BOLD, 280 ASS_DEFAULT_ITALIC, 281 ASS_DEFAULT_UNDERLINE, 282 3, 283 ASS_DEFAULT_ALIGNMENT); 284 if (ret < 0) { 285 return ret; 286 } 287 288 return ret; 289} 290 291static av_cold int close_decoder(AVCodecContext *avctx) 292{ 293 CCaptionSubContext *ctx = avctx->priv_data; 294 av_bprint_finalize(&ctx->buffer[0], NULL); 295 av_bprint_finalize(&ctx->buffer[1], NULL); 296 return 0; 297} 298 299static void flush_decoder(AVCodecContext *avctx) 300{ 301 CCaptionSubContext *ctx = avctx->priv_data; 302 ctx->screen[0].row_used = 0; 303 ctx->screen[1].row_used = 0; 304 ctx->prev_cmd[0] = 0; 305 ctx->prev_cmd[1] = 0; 306 ctx->mode = CCMODE_ROLLUP; 307 ctx->rollup = 2; 308 ctx->cursor_row = 10; 309 ctx->cursor_column = 0; 310 ctx->cursor_font = 0; 311 ctx->cursor_color = 0; 312 ctx->bg_color = CCCOL_BLACK; 313 ctx->cursor_charset = 0; 314 ctx->active_screen = 0; 315 ctx->last_real_time = 0; 316 ctx->screen_touched = 0; 317 ctx->buffer_changed = 0; 318 if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP)) 319 ctx->readorder = 0; 320 av_bprint_clear(&ctx->buffer[0]); 321 av_bprint_clear(&ctx->buffer[1]); 322} 323 324/** 325 * @param ctx closed caption context just to print log 326 */ 327static void write_char(CCaptionSubContext *ctx, struct Screen *screen, char ch) 328{ 329 uint8_t col = ctx->cursor_column; 330 char *row = screen->characters[ctx->cursor_row]; 331 char *font = screen->fonts[ctx->cursor_row]; 332 char *color = screen->colors[ctx->cursor_row]; 333 char *bg = screen->bgs[ctx->cursor_row]; 334 char *charset = screen->charsets[ctx->cursor_row]; 335 336 if (col < SCREEN_COLUMNS) { 337 row[col] = ch; 338 font[col] = ctx->cursor_font; 339 color[col] = ctx->cursor_color; 340 bg[col] = ctx->bg_color; 341 charset[col] = ctx->cursor_charset; 342 ctx->cursor_charset = CCSET_BASIC_AMERICAN; 343 if (ch) ctx->cursor_column++; 344 return; 345 } 346 /* We have extra space at end only for null character */ 347 else if (col == SCREEN_COLUMNS && ch == 0) { 348 row[col] = ch; 349 return; 350 } 351 else { 352 av_log(ctx, AV_LOG_WARNING, "Data Ignored since exceeding screen width\n"); 353 return; 354 } 355} 356 357/** 358 * This function after validating parity bit, also remove it from data pair. 359 * The first byte doesn't pass parity, we replace it with a solid blank 360 * and process the pair. 361 * If the second byte doesn't pass parity, it returns INVALIDDATA 362 * user can ignore the whole pair and pass the other pair. 363 */ 364static int validate_cc_data_pair(const uint8_t *cc_data_pair, uint8_t *hi) 365{ 366 uint8_t cc_valid = (*cc_data_pair & 4) >>2; 367 uint8_t cc_type = *cc_data_pair & 3; 368 369 *hi = cc_data_pair[1]; 370 371 if (!cc_valid) 372 return AVERROR_INVALIDDATA; 373 374 // if EIA-608 data then verify parity. 375 if (cc_type==0 || cc_type==1) { 376 if (!av_parity(cc_data_pair[2])) { 377 return AVERROR_INVALIDDATA; 378 } 379 if (!av_parity(cc_data_pair[1])) { 380 *hi = 0x7F; 381 } 382 } 383 384 //Skip non-data 385 if ((cc_data_pair[0] == 0xFA || cc_data_pair[0] == 0xFC || cc_data_pair[0] == 0xFD) 386 && (cc_data_pair[1] & 0x7F) == 0 && (cc_data_pair[2] & 0x7F) == 0) 387 return AVERROR_PATCHWELCOME; 388 389 //skip 708 data 390 if (cc_type == 3 || cc_type == 2) 391 return AVERROR_PATCHWELCOME; 392 393 return 0; 394} 395 396static struct Screen *get_writing_screen(CCaptionSubContext *ctx) 397{ 398 switch (ctx->mode) { 399 case CCMODE_POPON: 400 // use Inactive screen 401 return ctx->screen + !ctx->active_screen; 402 case CCMODE_PAINTON: 403 case CCMODE_ROLLUP: 404 case CCMODE_TEXT: 405 // use active screen 406 return ctx->screen + ctx->active_screen; 407 } 408 /* It was never an option */ 409 return NULL; 410} 411 412static void roll_up(CCaptionSubContext *ctx) 413{ 414 struct Screen *screen; 415 int i, keep_lines; 416 417 if (ctx->mode == CCMODE_TEXT) 418 return; 419 420 screen = get_writing_screen(ctx); 421 422 /* +1 signify cursor_row starts from 0 423 * Can't keep lines less then row cursor pos 424 */ 425 keep_lines = FFMIN(ctx->cursor_row + 1, ctx->rollup); 426 427 for (i = 0; i < SCREEN_ROWS; i++) { 428 if (i > ctx->cursor_row - keep_lines && i <= ctx->cursor_row) 429 continue; 430 UNSET_FLAG(screen->row_used, i); 431 } 432 433 for (i = 0; i < keep_lines && screen->row_used; i++) { 434 const int i_row = ctx->cursor_row - keep_lines + i + 1; 435 436 memcpy(screen->characters[i_row], screen->characters[i_row+1], SCREEN_COLUMNS); 437 memcpy(screen->colors[i_row], screen->colors[i_row+1], SCREEN_COLUMNS); 438 memcpy(screen->bgs[i_row], screen->bgs[i_row+1], SCREEN_COLUMNS); 439 memcpy(screen->fonts[i_row], screen->fonts[i_row+1], SCREEN_COLUMNS); 440 memcpy(screen->charsets[i_row], screen->charsets[i_row+1], SCREEN_COLUMNS); 441 if (CHECK_FLAG(screen->row_used, i_row + 1)) 442 SET_FLAG(screen->row_used, i_row); 443 } 444 445 UNSET_FLAG(screen->row_used, ctx->cursor_row); 446} 447 448static int capture_screen(CCaptionSubContext *ctx) 449{ 450 int i, j, tab = 0; 451 struct Screen *screen = ctx->screen + ctx->active_screen; 452 enum cc_font prev_font = CCFONT_REGULAR; 453 enum cc_color_code prev_color = CCCOL_WHITE; 454 enum cc_color_code prev_bg_color = CCCOL_BLACK; 455 const int bidx = ctx->buffer_index; 456 457 av_bprint_clear(&ctx->buffer[bidx]); 458 459 for (i = 0; screen->row_used && i < SCREEN_ROWS; i++) 460 { 461 if (CHECK_FLAG(screen->row_used, i)) { 462 const char *row = screen->characters[i]; 463 const char *charset = screen->charsets[i]; 464 j = 0; 465 while (row[j] == ' ' && charset[j] == CCSET_BASIC_AMERICAN) 466 j++; 467 if (!tab || j < tab) 468 tab = j; 469 } 470 } 471 472 for (i = 0; screen->row_used && i < SCREEN_ROWS; i++) 473 { 474 if (CHECK_FLAG(screen->row_used, i)) { 475 const char *row = screen->characters[i]; 476 const char *font = screen->fonts[i]; 477 const char *bg = screen->bgs[i]; 478 const char *color = screen->colors[i]; 479 const char *charset = screen->charsets[i]; 480 const char *override; 481 int x, y, seen_char = 0; 482 j = 0; 483 484 /* skip leading space */ 485 while (row[j] == ' ' && charset[j] == CCSET_BASIC_AMERICAN && j < tab) 486 j++; 487 488 x = ASS_DEFAULT_PLAYRESX * (0.1 + 0.0250 * j); 489 y = ASS_DEFAULT_PLAYRESY * (0.1 + 0.0533 * i); 490 av_bprintf(&ctx->buffer[bidx], "{\\an7}{\\pos(%d,%d)}", x, y); 491 492 for (; j < SCREEN_COLUMNS; j++) { 493 const char *e_tag = "", *s_tag = "", *c_tag = "", *b_tag = ""; 494 495 if (row[j] == 0) 496 break; 497 498 if (prev_font != font[j]) { 499 switch (prev_font) { 500 case CCFONT_ITALICS: 501 e_tag = "{\\i0}"; 502 break; 503 case CCFONT_UNDERLINED: 504 e_tag = "{\\u0}"; 505 break; 506 case CCFONT_UNDERLINED_ITALICS: 507 e_tag = "{\\u0}{\\i0}"; 508 break; 509 } 510 switch (font[j]) { 511 case CCFONT_ITALICS: 512 s_tag = "{\\i1}"; 513 break; 514 case CCFONT_UNDERLINED: 515 s_tag = "{\\u1}"; 516 break; 517 case CCFONT_UNDERLINED_ITALICS: 518 s_tag = "{\\u1}{\\i1}"; 519 break; 520 } 521 } 522 if (prev_color != color[j]) { 523 switch (color[j]) { 524 case CCCOL_WHITE: 525 c_tag = "{\\c&HFFFFFF&}"; 526 break; 527 case CCCOL_GREEN: 528 c_tag = "{\\c&H00FF00&}"; 529 break; 530 case CCCOL_BLUE: 531 c_tag = "{\\c&HFF0000&}"; 532 break; 533 case CCCOL_CYAN: 534 c_tag = "{\\c&HFFFF00&}"; 535 break; 536 case CCCOL_RED: 537 c_tag = "{\\c&H0000FF&}"; 538 break; 539 case CCCOL_YELLOW: 540 c_tag = "{\\c&H00FFFF&}"; 541 break; 542 case CCCOL_MAGENTA: 543 c_tag = "{\\c&HFF00FF&}"; 544 break; 545 } 546 } 547 if (prev_bg_color != bg[j]) { 548 switch (bg[j]) { 549 case CCCOL_WHITE: 550 b_tag = "{\\3c&HFFFFFF&}"; 551 break; 552 case CCCOL_GREEN: 553 b_tag = "{\\3c&H00FF00&}"; 554 break; 555 case CCCOL_BLUE: 556 b_tag = "{\\3c&HFF0000&}"; 557 break; 558 case CCCOL_CYAN: 559 b_tag = "{\\3c&HFFFF00&}"; 560 break; 561 case CCCOL_RED: 562 b_tag = "{\\3c&H0000FF&}"; 563 break; 564 case CCCOL_YELLOW: 565 b_tag = "{\\3c&H00FFFF&}"; 566 break; 567 case CCCOL_MAGENTA: 568 b_tag = "{\\3c&HFF00FF&}"; 569 break; 570 case CCCOL_BLACK: 571 b_tag = "{\\3c&H000000&}"; 572 break; 573 } 574 } 575 576 prev_font = font[j]; 577 prev_color = color[j]; 578 prev_bg_color = bg[j]; 579 override = charset_overrides[(int)charset[j]][(int)row[j]]; 580 if (override) { 581 av_bprintf(&ctx->buffer[bidx], "%s%s%s%s%s", e_tag, s_tag, c_tag, b_tag, override); 582 seen_char = 1; 583 } else if (row[j] == ' ' && !seen_char) { 584 av_bprintf(&ctx->buffer[bidx], "%s%s%s%s\\h", e_tag, s_tag, c_tag, b_tag); 585 } else { 586 av_bprintf(&ctx->buffer[bidx], "%s%s%s%s%c", e_tag, s_tag, c_tag, b_tag, row[j]); 587 seen_char = 1; 588 } 589 590 } 591 av_bprintf(&ctx->buffer[bidx], "\\N"); 592 } 593 } 594 if (!av_bprint_is_complete(&ctx->buffer[bidx])) 595 return AVERROR(ENOMEM); 596 if (screen->row_used && ctx->buffer[bidx].len >= 2) { 597 ctx->buffer[bidx].len -= 2; 598 ctx->buffer[bidx].str[ctx->buffer[bidx].len] = 0; 599 } 600 ctx->buffer_changed = 1; 601 return 0; 602} 603 604static void update_time(CCaptionSubContext *ctx, int64_t pts) 605{ 606 ctx->buffer_time[0] = ctx->buffer_time[1]; 607 ctx->buffer_time[1] = pts; 608} 609 610static void handle_bgattr(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo) 611{ 612 const int i = (lo & 0xf) >> 1; 613 614 ctx->bg_color = bg_attribs[i]; 615} 616 617static void handle_textattr(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo) 618{ 619 int i = lo - 0x20; 620 struct Screen *screen = get_writing_screen(ctx); 621 622 if (i >= 32) 623 return; 624 625 ctx->cursor_color = pac2_attribs[i][0]; 626 ctx->cursor_font = pac2_attribs[i][1]; 627 628 SET_FLAG(screen->row_used, ctx->cursor_row); 629 write_char(ctx, screen, ' '); 630} 631 632static void handle_pac(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo) 633{ 634 static const int8_t row_map[] = { 635 11, -1, 1, 2, 3, 4, 12, 13, 14, 15, 5, 6, 7, 8, 9, 10 636 }; 637 const int index = ( (hi<<1) & 0x0e) | ( (lo>>5) & 0x01 ); 638 struct Screen *screen = get_writing_screen(ctx); 639 int indent, i; 640 641 if (row_map[index] <= 0) { 642 av_log(ctx, AV_LOG_DEBUG, "Invalid pac index encountered\n"); 643 return; 644 } 645 646 lo &= 0x1f; 647 648 ctx->cursor_row = row_map[index] - 1; 649 ctx->cursor_color = pac2_attribs[lo][0]; 650 ctx->cursor_font = pac2_attribs[lo][1]; 651 ctx->cursor_charset = CCSET_BASIC_AMERICAN; 652 ctx->cursor_column = 0; 653 indent = pac2_attribs[lo][2]; 654 for (i = 0; i < indent; i++) { 655 write_char(ctx, screen, ' '); 656 } 657} 658 659static int handle_edm(CCaptionSubContext *ctx) 660{ 661 struct Screen *screen = ctx->screen + ctx->active_screen; 662 int ret; 663 664 // In buffered mode, keep writing to screen until it is wiped. 665 // Before wiping the display, capture contents to emit subtitle. 666 if (!ctx->real_time) 667 ret = capture_screen(ctx); 668 669 screen->row_used = 0; 670 ctx->bg_color = CCCOL_BLACK; 671 672 // In realtime mode, emit an empty caption so the last one doesn't 673 // stay on the screen. 674 if (ctx->real_time) 675 ret = capture_screen(ctx); 676 677 return ret; 678} 679 680static int handle_eoc(CCaptionSubContext *ctx) 681{ 682 int ret; 683 684 ctx->active_screen = !ctx->active_screen; 685 686 // In buffered mode, we wait til the *next* EOC and 687 // capture what was already on the screen since the last EOC. 688 if (!ctx->real_time) 689 ret = handle_edm(ctx); 690 691 ctx->cursor_column = 0; 692 693 // In realtime mode, we display the buffered contents (after 694 // flipping the buffer to active above) as soon as EOC arrives. 695 if (ctx->real_time) 696 ret = capture_screen(ctx); 697 698 return ret; 699} 700 701static void handle_delete_end_of_row(CCaptionSubContext *ctx) 702{ 703 struct Screen *screen = get_writing_screen(ctx); 704 write_char(ctx, screen, 0); 705} 706 707static void handle_char(CCaptionSubContext *ctx, char hi, char lo) 708{ 709 struct Screen *screen = get_writing_screen(ctx); 710 711 SET_FLAG(screen->row_used, ctx->cursor_row); 712 713 switch (hi) { 714 case 0x11: 715 ctx->cursor_charset = CCSET_SPECIAL_AMERICAN; 716 break; 717 case 0x12: 718 if (ctx->cursor_column > 0) 719 ctx->cursor_column -= 1; 720 ctx->cursor_charset = CCSET_EXTENDED_SPANISH_FRENCH_MISC; 721 break; 722 case 0x13: 723 if (ctx->cursor_column > 0) 724 ctx->cursor_column -= 1; 725 ctx->cursor_charset = CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH; 726 break; 727 default: 728 ctx->cursor_charset = CCSET_BASIC_AMERICAN; 729 write_char(ctx, screen, hi); 730 break; 731 } 732 733 if (lo) { 734 write_char(ctx, screen, lo); 735 } 736 write_char(ctx, screen, 0); 737 738 if (ctx->mode != CCMODE_POPON) 739 ctx->screen_touched = 1; 740 741 if (lo) 742 ff_dlog(ctx, "(%c,%c)\n", hi, lo); 743 else 744 ff_dlog(ctx, "(%c)\n", hi); 745} 746 747static int process_cc608(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo) 748{ 749 int ret = 0; 750 751 if (hi == ctx->prev_cmd[0] && lo == ctx->prev_cmd[1]) { 752 return 0; 753 } 754 755 /* set prev command */ 756 ctx->prev_cmd[0] = hi; 757 ctx->prev_cmd[1] = lo; 758 759 if ( (hi == 0x10 && (lo >= 0x40 && lo <= 0x5f)) || 760 ( (hi >= 0x11 && hi <= 0x17) && (lo >= 0x40 && lo <= 0x7f) ) ) { 761 handle_pac(ctx, hi, lo); 762 } else if ( ( hi == 0x11 && lo >= 0x20 && lo <= 0x2f ) || 763 ( hi == 0x17 && lo >= 0x2e && lo <= 0x2f) ) { 764 handle_textattr(ctx, hi, lo); 765 } else if ((hi == 0x10 && lo >= 0x20 && lo <= 0x2f)) { 766 handle_bgattr(ctx, hi, lo); 767 } else if (hi == 0x14 || hi == 0x15 || hi == 0x1c) { 768 switch (lo) { 769 case 0x20: 770 /* resume caption loading */ 771 ctx->mode = CCMODE_POPON; 772 break; 773 case 0x24: 774 handle_delete_end_of_row(ctx); 775 break; 776 case 0x25: 777 case 0x26: 778 case 0x27: 779 ctx->rollup = lo - 0x23; 780 ctx->mode = CCMODE_ROLLUP; 781 break; 782 case 0x29: 783 /* resume direct captioning */ 784 ctx->mode = CCMODE_PAINTON; 785 break; 786 case 0x2b: 787 /* resume text display */ 788 ctx->mode = CCMODE_TEXT; 789 break; 790 case 0x2c: 791 /* erase display memory */ 792 handle_edm(ctx); 793 break; 794 case 0x2d: 795 /* carriage return */ 796 ff_dlog(ctx, "carriage return\n"); 797 if (!ctx->real_time) 798 ret = capture_screen(ctx); 799 roll_up(ctx); 800 ctx->cursor_column = 0; 801 break; 802 case 0x2e: 803 /* erase buffered (non displayed) memory */ 804 // Only in realtime mode. In buffered mode, we re-use the inactive screen 805 // for our own buffering. 806 if (ctx->real_time) { 807 struct Screen *screen = ctx->screen + !ctx->active_screen; 808 screen->row_used = 0; 809 } 810 break; 811 case 0x2f: 812 /* end of caption */ 813 ff_dlog(ctx, "handle_eoc\n"); 814 ret = handle_eoc(ctx); 815 break; 816 default: 817 ff_dlog(ctx, "Unknown command 0x%hhx 0x%hhx\n", hi, lo); 818 break; 819 } 820 } else if (hi >= 0x11 && hi <= 0x13) { 821 /* Special characters */ 822 handle_char(ctx, hi, lo); 823 } else if (hi >= 0x20) { 824 /* Standard characters (always in pairs) */ 825 handle_char(ctx, hi, lo); 826 ctx->prev_cmd[0] = ctx->prev_cmd[1] = 0; 827 } else if (hi == 0x17 && lo >= 0x21 && lo <= 0x23) { 828 int i; 829 /* Tab offsets (spacing) */ 830 for (i = 0; i < lo - 0x20; i++) { 831 handle_char(ctx, ' ', 0); 832 } 833 } else { 834 /* Ignoring all other non data code */ 835 ff_dlog(ctx, "Unknown command 0x%hhx 0x%hhx\n", hi, lo); 836 } 837 838 return ret; 839} 840 841static int decode(AVCodecContext *avctx, AVSubtitle *sub, 842 int *got_sub, const AVPacket *avpkt) 843{ 844 CCaptionSubContext *ctx = avctx->priv_data; 845 int64_t in_time = sub->pts; 846 int64_t start_time; 847 int64_t end_time; 848 int bidx = ctx->buffer_index; 849 const uint8_t *bptr = avpkt->data; 850 int len = avpkt->size; 851 int ret = 0; 852 int i; 853 unsigned nb_rect_allocated = 0; 854 855 for (i = 0; i < len; i += 3) { 856 uint8_t hi, cc_type = bptr[i] & 1; 857 858 if (ctx->data_field < 0) 859 ctx->data_field = cc_type; 860 861 if (validate_cc_data_pair(bptr + i, &hi)) 862 continue; 863 864 if (cc_type != ctx->data_field) 865 continue; 866 867 ret = process_cc608(ctx, hi & 0x7f, bptr[i + 2] & 0x7f); 868 if (ret < 0) 869 return ret; 870 871 if (!ctx->buffer_changed) 872 continue; 873 ctx->buffer_changed = 0; 874 875 if (!ctx->real_time && ctx->mode == CCMODE_POPON) 876 ctx->buffer_index = bidx = !ctx->buffer_index; 877 878 update_time(ctx, in_time); 879 880 if (ctx->buffer[bidx].str[0] || ctx->real_time) { 881 ff_dlog(ctx, "cdp writing data (%s)\n", ctx->buffer[bidx].str); 882 start_time = ctx->buffer_time[0]; 883 sub->pts = start_time; 884 end_time = ctx->buffer_time[1]; 885 if (!ctx->real_time) 886 sub->end_display_time = av_rescale_q(end_time - start_time, 887 AV_TIME_BASE_Q, ms_tb); 888 else 889 sub->end_display_time = -1; 890 ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated); 891 if (ret < 0) 892 return ret; 893 ctx->last_real_time = sub->pts; 894 ctx->screen_touched = 0; 895 } 896 } 897 898 if (!bptr && !ctx->real_time && ctx->buffer[!ctx->buffer_index].str[0]) { 899 bidx = !ctx->buffer_index; 900 ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated); 901 if (ret < 0) 902 return ret; 903 sub->pts = ctx->buffer_time[1]; 904 sub->end_display_time = av_rescale_q(ctx->buffer_time[1] - ctx->buffer_time[0], 905 AV_TIME_BASE_Q, ms_tb); 906 if (sub->end_display_time == 0) 907 sub->end_display_time = ctx->buffer[bidx].len * 20; 908 } 909 910 if (ctx->real_time && ctx->screen_touched && 911 sub->pts >= ctx->last_real_time + av_rescale_q(ctx->real_time_latency_msec, ms_tb, AV_TIME_BASE_Q)) { 912 ctx->last_real_time = sub->pts; 913 ctx->screen_touched = 0; 914 915 capture_screen(ctx); 916 ctx->buffer_changed = 0; 917 918 ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated); 919 if (ret < 0) 920 return ret; 921 sub->end_display_time = -1; 922 } 923 924 *got_sub = sub->num_rects > 0; 925 return ret; 926} 927 928#define OFFSET(x) offsetof(CCaptionSubContext, x) 929#define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM 930static const AVOption options[] = { 931 { "real_time", "emit subtitle events as they are decoded for real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD }, 932 { "real_time_latency_msec", "minimum elapsed time between emitting real-time subtitle events", OFFSET(real_time_latency_msec), AV_OPT_TYPE_INT, { .i64 = 200 }, 0, 500, SD }, 933 { "data_field", "select data field", OFFSET(data_field), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, SD, "data_field" }, 934 { "auto", "pick first one that appears", 0, AV_OPT_TYPE_CONST, { .i64 =-1 }, 0, 0, SD, "data_field" }, 935 { "first", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, SD, "data_field" }, 936 { "second", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, SD, "data_field" }, 937 {NULL} 938}; 939 940static const AVClass ccaption_dec_class = { 941 .class_name = "Closed caption Decoder", 942 .item_name = av_default_item_name, 943 .option = options, 944 .version = LIBAVUTIL_VERSION_INT, 945}; 946 947const FFCodec ff_ccaption_decoder = { 948 .p.name = "cc_dec", 949 .p.long_name = NULL_IF_CONFIG_SMALL("Closed Caption (EIA-608 / CEA-708)"), 950 .p.type = AVMEDIA_TYPE_SUBTITLE, 951 .p.id = AV_CODEC_ID_EIA_608, 952 .p.priv_class = &ccaption_dec_class, 953 .p.capabilities = AV_CODEC_CAP_DELAY, 954 .priv_data_size = sizeof(CCaptionSubContext), 955 .init = init_decoder, 956 .close = close_decoder, 957 .flush = flush_decoder, 958 FF_CODEC_DECODE_SUB_CB(decode), 959 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 960}; 961