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