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