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.

896 lines
28KB

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