ccaption_dec.c 34 KB

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