LCOV - code coverage report
Current view: top level - libavcodec - ccaption_dec.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 255 344 74.1 %
Date: 2017-12-17 23:02:56 Functions: 15 17 88.2 %

          Line data    Source code
       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 {
      40             :     CCMODE_POPON,
      41             :     CCMODE_PAINTON,
      42             :     CCMODE_ROLLUP,
      43             :     CCMODE_TEXT,
      44             : };
      45             : 
      46             : enum cc_color_code {
      47             :     CCCOL_WHITE,
      48             :     CCCOL_GREEN,
      49             :     CCCOL_BLUE,
      50             :     CCCOL_CYAN,
      51             :     CCCOL_RED,
      52             :     CCCOL_YELLOW,
      53             :     CCCOL_MAGENTA,
      54             :     CCCOL_USERDEFINED,
      55             :     CCCOL_BLACK,
      56             :     CCCOL_TRANSPARENT,
      57             : };
      58             : 
      59             : enum cc_font {
      60             :     CCFONT_REGULAR,
      61             :     CCFONT_ITALICS,
      62             :     CCFONT_UNDERLINED,
      63             :     CCFONT_UNDERLINED_ITALICS,
      64             : };
      65             : 
      66             : enum cc_charset {
      67             :     CCSET_BASIC_AMERICAN,
      68             :     CCSET_SPECIAL_AMERICAN,
      69             :     CCSET_EXTENDED_SPANISH_FRENCH_MISC,
      70             :     CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH,
      71             : };
      72             : 
      73             : static const char *charset_overrides[4][128] =
      74             : {
      75             :     [CCSET_BASIC_AMERICAN] = {
      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             :     },
      88             :     [CCSET_SPECIAL_AMERICAN] = {
      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             :     },
     106             :     [CCSET_EXTENDED_SPANISH_FRENCH_MISC] = {
     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             :     },
     140             :     [CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH] = {
     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 */
     215             :     uint8_t characters[SCREEN_ROWS][SCREEN_COLUMNS+1];
     216             :     uint8_t charsets[SCREEN_ROWS][SCREEN_COLUMNS+1];
     217             :     uint8_t colors[SCREEN_ROWS][SCREEN_COLUMNS+1];
     218             :     uint8_t fonts[SCREEN_ROWS][SCREEN_COLUMNS+1];
     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;
     230             :     int real_time;
     231             :     struct Screen screen[2];
     232             :     int active_screen;
     233             :     uint8_t cursor_row;
     234             :     uint8_t cursor_column;
     235             :     uint8_t cursor_color;
     236             :     uint8_t cursor_font;
     237             :     uint8_t cursor_charset;
     238             :     AVBPrint buffer;
     239             :     int buffer_changed;
     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;
     246             :     int screen_touched;
     247             :     int64_t last_real_time;
     248             :     char prev_cmd[2];
     249             :     /* buffer to store pkt data */
     250             :     uint8_t *pktbuf;
     251             :     int pktbuf_size;
     252             :     int readorder;
     253             : } CCaptionSubContext;
     254             : 
     255             : 
     256           8 : static av_cold int init_decoder(AVCodecContext *avctx)
     257             : {
     258             :     int ret;
     259           8 :     CCaptionSubContext *ctx = avctx->priv_data;
     260             : 
     261           8 :     av_bprint_init(&ctx->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
     262             :     /* taking by default roll up to 2 */
     263           8 :     ctx->mode = CCMODE_ROLLUP;
     264           8 :     ctx->rollup = 2;
     265           8 :     ctx->cursor_row = 10;
     266           8 :     ret = ff_ass_subtitle_header(avctx, "Monospace",
     267             :                                  ASS_DEFAULT_FONT_SIZE,
     268             :                                  ASS_DEFAULT_COLOR,
     269             :                                  ASS_DEFAULT_BACK_COLOR,
     270             :                                  ASS_DEFAULT_BOLD,
     271             :                                  ASS_DEFAULT_ITALIC,
     272             :                                  ASS_DEFAULT_UNDERLINE,
     273             :                                  3,
     274             :                                  ASS_DEFAULT_ALIGNMENT);
     275           8 :     if (ret < 0) {
     276           0 :         return ret;
     277             :     }
     278             : 
     279           8 :     return ret;
     280             : }
     281             : 
     282           8 : static av_cold int close_decoder(AVCodecContext *avctx)
     283             : {
     284           8 :     CCaptionSubContext *ctx = avctx->priv_data;
     285           8 :     av_bprint_finalize(&ctx->buffer, NULL);
     286           8 :     av_freep(&ctx->pktbuf);
     287           8 :     ctx->pktbuf_size = 0;
     288           8 :     return 0;
     289             : }
     290             : 
     291           0 : static void flush_decoder(AVCodecContext *avctx)
     292             : {
     293           0 :     CCaptionSubContext *ctx = avctx->priv_data;
     294           0 :     ctx->screen[0].row_used = 0;
     295           0 :     ctx->screen[1].row_used = 0;
     296           0 :     ctx->prev_cmd[0] = 0;
     297           0 :     ctx->prev_cmd[1] = 0;
     298           0 :     ctx->mode = CCMODE_ROLLUP;
     299           0 :     ctx->rollup = 2;
     300           0 :     ctx->cursor_row = 10;
     301           0 :     ctx->cursor_column = 0;
     302           0 :     ctx->cursor_font = 0;
     303           0 :     ctx->cursor_color = 0;
     304           0 :     ctx->cursor_charset = 0;
     305           0 :     ctx->active_screen = 0;
     306           0 :     ctx->last_real_time = 0;
     307           0 :     ctx->screen_touched = 0;
     308           0 :     ctx->buffer_changed = 0;
     309           0 :     if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
     310           0 :         ctx->readorder = 0;
     311           0 :     av_bprint_clear(&ctx->buffer);
     312           0 : }
     313             : 
     314             : /**
     315             :  * @param ctx closed caption context just to print log
     316             :  */
     317        5815 : static void write_char(CCaptionSubContext *ctx, struct Screen *screen, char ch)
     318             : {
     319        5815 :     uint8_t col = ctx->cursor_column;
     320        5815 :     char *row = screen->characters[ctx->cursor_row];
     321        5815 :     char *font = screen->fonts[ctx->cursor_row];
     322        5815 :     char *charset = screen->charsets[ctx->cursor_row];
     323             : 
     324        5815 :     if (col < SCREEN_COLUMNS) {
     325        5808 :         row[col] = ch;
     326        5808 :         font[col] = ctx->cursor_font;
     327        5808 :         charset[col] = ctx->cursor_charset;
     328        5808 :         ctx->cursor_charset = CCSET_BASIC_AMERICAN;
     329        5808 :         if (ch) ctx->cursor_column++;
     330        5808 :         return;
     331             :     }
     332             :     /* We have extra space at end only for null character */
     333           7 :     else if (col == SCREEN_COLUMNS && ch == 0) {
     334           7 :         row[col] = ch;
     335           7 :         return;
     336             :     }
     337             :     else {
     338           0 :         av_log(ctx, AV_LOG_WARNING, "Data Ignored since exceeding screen width\n");
     339           0 :         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        8253 : static int validate_cc_data_pair(uint8_t *cc_data_pair)
     351             : {
     352        8253 :     uint8_t cc_valid = (*cc_data_pair & 4) >>2;
     353        8253 :     uint8_t cc_type = *cc_data_pair & 3;
     354             : 
     355        8253 :     if (!cc_valid)
     356        4582 :         return AVERROR_INVALIDDATA;
     357             : 
     358             :     // if EIA-608 data then verify parity.
     359        3671 :     if (cc_type==0 || cc_type==1) {
     360        3393 :         if (!av_parity(cc_data_pair[2])) {
     361           0 :             return AVERROR_INVALIDDATA;
     362             :         }
     363        3393 :         if (!av_parity(cc_data_pair[1])) {
     364           0 :             cc_data_pair[1]=0x7F;
     365             :         }
     366             :     }
     367             : 
     368             :     //Skip non-data
     369        3671 :     if ((cc_data_pair[0] == 0xFA || cc_data_pair[0] == 0xFC || cc_data_pair[0] == 0xFD)
     370        2989 :          && (cc_data_pair[1] & 0x7F) == 0 && (cc_data_pair[2] & 0x7F) == 0)
     371         434 :         return AVERROR_PATCHWELCOME;
     372             : 
     373             :     //skip 708 data
     374        3237 :     if (cc_type == 3 || cc_type == 2)
     375         278 :         return AVERROR_PATCHWELCOME;
     376             : 
     377             :     /* remove parity bit */
     378        2959 :     cc_data_pair[1] &= 0x7F;
     379        2959 :     cc_data_pair[2] &= 0x7F;
     380             : 
     381        2959 :     return 0;
     382             : }
     383             : 
     384        1811 : static struct Screen *get_writing_screen(CCaptionSubContext *ctx)
     385             : {
     386        1811 :     switch (ctx->mode) {
     387        1676 :     case CCMODE_POPON:
     388             :         // use Inactive screen
     389        1676 :         return ctx->screen + !ctx->active_screen;
     390         135 :     case CCMODE_PAINTON:
     391             :     case CCMODE_ROLLUP:
     392             :     case CCMODE_TEXT:
     393             :         // use active screen
     394         135 :         return ctx->screen + ctx->active_screen;
     395             :     }
     396             :     /* It was never an option */
     397           0 :     return NULL;
     398             : }
     399             : 
     400           9 : static void roll_up(CCaptionSubContext *ctx)
     401             : {
     402             :     struct Screen *screen;
     403             :     int i, keep_lines;
     404             : 
     405           9 :     if (ctx->mode == CCMODE_TEXT)
     406           0 :         return;
     407             : 
     408           9 :     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           9 :     keep_lines = FFMIN(ctx->cursor_row + 1, ctx->rollup);
     414             : 
     415         144 :     for (i = 0; i < SCREEN_ROWS; i++) {
     416         135 :         if (i > ctx->cursor_row - keep_lines && i <= ctx->cursor_row)
     417          18 :             continue;
     418         117 :         UNSET_FLAG(screen->row_used, i);
     419             :     }
     420             : 
     421          23 :     for (i = 0; i < keep_lines && screen->row_used; i++) {
     422          14 :         const int i_row = ctx->cursor_row - keep_lines + i + 1;
     423             : 
     424          14 :         memcpy(screen->characters[i_row], screen->characters[i_row+1], SCREEN_COLUMNS);
     425          14 :         memcpy(screen->colors[i_row], screen->colors[i_row+1], SCREEN_COLUMNS);
     426          14 :         memcpy(screen->fonts[i_row], screen->fonts[i_row+1], SCREEN_COLUMNS);
     427          14 :         memcpy(screen->charsets[i_row], screen->charsets[i_row+1], SCREEN_COLUMNS);
     428          14 :         if (CHECK_FLAG(screen->row_used, i_row + 1))
     429           7 :             SET_FLAG(screen->row_used, i_row);
     430             :     }
     431             : 
     432           9 :     UNSET_FLAG(screen->row_used, ctx->cursor_row);
     433             : }
     434             : 
     435         233 : static int capture_screen(CCaptionSubContext *ctx)
     436             : {
     437         233 :     int i, j, tab = 0;
     438         233 :     struct Screen *screen = ctx->screen + ctx->active_screen;
     439         233 :     enum cc_font prev_font = CCFONT_REGULAR;
     440         233 :     av_bprint_clear(&ctx->buffer);
     441             : 
     442        2138 :     for (i = 0; screen->row_used && i < SCREEN_ROWS; i++)
     443             :     {
     444        1905 :         if (CHECK_FLAG(screen->row_used, i)) {
     445         242 :             const char *row = screen->characters[i];
     446         242 :             const char *charset = screen->charsets[i];
     447         242 :             j = 0;
     448        1648 :             while (row[j] == ' ' && charset[j] == CCSET_BASIC_AMERICAN)
     449        1164 :                 j++;
     450         242 :             if (!tab || j < tab)
     451         190 :                 tab = j;
     452             :         }
     453             :     }
     454             : 
     455        2138 :     for (i = 0; screen->row_used && i < SCREEN_ROWS; i++)
     456             :     {
     457        1905 :         if (CHECK_FLAG(screen->row_used, i)) {
     458         242 :             const char *row = screen->characters[i];
     459         242 :             const char *font = screen->fonts[i];
     460         242 :             const char *charset = screen->charsets[i];
     461             :             const char *override;
     462         242 :             int x, y, seen_char = 0;
     463         242 :             j = 0;
     464             : 
     465             :             /* skip leading space */
     466        1455 :             while (row[j] == ' ' && charset[j] == CCSET_BASIC_AMERICAN && j < tab)
     467         971 :                 j++;
     468             : 
     469         242 :             x = ASS_DEFAULT_PLAYRESX * (0.1 + 0.0250 * j);
     470         242 :             y = ASS_DEFAULT_PLAYRESY * (0.1 + 0.0533 * i);
     471         242 :             av_bprintf(&ctx->buffer, "{\\an7}{\\pos(%d,%d)}", x, y);
     472             : 
     473        4390 :             for (; j < SCREEN_COLUMNS; j++) {
     474        4383 :                 const char *e_tag = "", *s_tag = "";
     475             : 
     476        4383 :                 if (row[j] == 0)
     477         235 :                     break;
     478             : 
     479        4148 :                 if (prev_font != font[j]) {
     480          74 :                     switch (prev_font) {
     481          22 :                     case CCFONT_ITALICS:
     482          22 :                         e_tag = "{\\i0}";
     483          22 :                         break;
     484           0 :                     case CCFONT_UNDERLINED:
     485           0 :                         e_tag = "{\\u0}";
     486           0 :                         break;
     487           0 :                     case CCFONT_UNDERLINED_ITALICS:
     488           0 :                         e_tag = "{\\u0}{\\i0}";
     489           0 :                         break;
     490             :                     }
     491          74 :                     switch (font[j]) {
     492          52 :                     case CCFONT_ITALICS:
     493          52 :                         s_tag = "{\\i1}";
     494          52 :                         break;
     495           0 :                     case CCFONT_UNDERLINED:
     496           0 :                         s_tag = "{\\u1}";
     497           0 :                         break;
     498           0 :                     case CCFONT_UNDERLINED_ITALICS:
     499           0 :                         s_tag = "{\\u1}{\\i1}";
     500           0 :                         break;
     501             :                     }
     502             :                 }
     503        4148 :                 prev_font = font[j];
     504        4148 :                 override = charset_overrides[(int)charset[j]][(int)row[j]];
     505        4148 :                 if (override) {
     506          11 :                     av_bprintf(&ctx->buffer, "%s%s%s", e_tag, s_tag, override);
     507          11 :                     seen_char = 1;
     508        4137 :                 } else if (row[j] == ' ' && !seen_char) {
     509         193 :                     av_bprintf(&ctx->buffer, "%s%s\\h", e_tag, s_tag);
     510             :                 } else {
     511        3944 :                     av_bprintf(&ctx->buffer, "%s%s%c", e_tag, s_tag, row[j]);
     512        3944 :                     seen_char = 1;
     513             :                 }
     514             : 
     515             :             }
     516         242 :             av_bprintf(&ctx->buffer, "\\N");
     517             :         }
     518             :     }
     519         233 :     if (!av_bprint_is_complete(&ctx->buffer))
     520           0 :         return AVERROR(ENOMEM);
     521         233 :     if (screen->row_used && ctx->buffer.len >= 2) {
     522         127 :         ctx->buffer.len -= 2;
     523         127 :         ctx->buffer.str[ctx->buffer.len] = 0;
     524             :     }
     525         233 :     ctx->buffer_changed = 1;
     526         233 :     return 0;
     527             : }
     528             : 
     529         203 : static int reap_screen(CCaptionSubContext *ctx, int64_t pts)
     530             : {
     531         203 :     ctx->start_time = ctx->startv_time;
     532         203 :     ctx->startv_time = pts;
     533         203 :     ctx->end_time = pts;
     534         203 :     return capture_screen(ctx);
     535             : }
     536             : 
     537          11 : static void handle_textattr(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo)
     538             : {
     539          11 :     int i = lo - 0x20;
     540          11 :     struct Screen *screen = get_writing_screen(ctx);
     541             : 
     542          11 :     if (i >= 32)
     543           0 :         return;
     544             : 
     545          11 :     ctx->cursor_color = pac2_attribs[i][0];
     546          11 :     ctx->cursor_font = pac2_attribs[i][1];
     547             : 
     548          11 :     SET_FLAG(screen->row_used, ctx->cursor_row);
     549          11 :     write_char(ctx, screen, ' ');
     550             : }
     551             : 
     552         197 : static void handle_pac(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo)
     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         197 :     const int index = ( (hi<<1) & 0x0e) | ( (lo>>5) & 0x01 );
     558         197 :     struct Screen *screen = get_writing_screen(ctx);
     559             :     int indent, i;
     560             : 
     561         197 :     if (row_map[index] <= 0) {
     562           0 :         av_log(ctx, AV_LOG_DEBUG, "Invalid pac index encountered\n");
     563           0 :         return;
     564             :     }
     565             : 
     566         197 :     lo &= 0x1f;
     567             : 
     568         197 :     ctx->cursor_row = row_map[index] - 1;
     569         197 :     ctx->cursor_color =  pac2_attribs[lo][0];
     570         197 :     ctx->cursor_font = pac2_attribs[lo][1];
     571         197 :     ctx->cursor_charset = CCSET_BASIC_AMERICAN;
     572         197 :     ctx->cursor_column = 0;
     573         197 :     indent = pac2_attribs[lo][2];
     574        1333 :     for (i = 0; i < indent; i++) {
     575        1136 :         write_char(ctx, screen, ' ');
     576             :     }
     577             : }
     578             : 
     579             : /**
     580             :  * @param pts it is required to set end time
     581             :  */
     582         197 : static void handle_edm(CCaptionSubContext *ctx, int64_t pts)
     583             : {
     584         197 :     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         197 :     if (!ctx->real_time)
     589         197 :         reap_screen(ctx, pts);
     590             : 
     591         197 :     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         197 :     if (ctx->real_time)
     596           0 :         reap_screen(ctx, pts);
     597         197 : }
     598             : 
     599          93 : 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          93 :     if (!ctx->real_time)
     604          93 :         handle_edm(ctx,pts);
     605             : 
     606          93 :     ctx->active_screen = !ctx->active_screen;
     607          93 :     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          93 :     if (ctx->real_time)
     612           0 :         reap_screen(ctx, pts);
     613          93 : }
     614             : 
     615           0 : static void handle_delete_end_of_row(CCaptionSubContext *ctx, char hi, char lo)
     616             : {
     617           0 :     struct Screen *screen = get_writing_screen(ctx);
     618           0 :     write_char(ctx, screen, 0);
     619           0 : }
     620             : 
     621        1594 : static void handle_char(CCaptionSubContext *ctx, char hi, char lo, int64_t pts)
     622             : {
     623        1594 :     struct Screen *screen = get_writing_screen(ctx);
     624             : 
     625        1594 :     SET_FLAG(screen->row_used, ctx->cursor_row);
     626             : 
     627        1594 :     switch (hi) {
     628           0 :       case 0x11:
     629           0 :         ctx->cursor_charset = CCSET_SPECIAL_AMERICAN;
     630           0 :         break;
     631           0 :       case 0x12:
     632           0 :         if (ctx->cursor_column > 0)
     633           0 :             ctx->cursor_column -= 1;
     634           0 :         ctx->cursor_charset = CCSET_EXTENDED_SPANISH_FRENCH_MISC;
     635           0 :         break;
     636           0 :       case 0x13:
     637           0 :         if (ctx->cursor_column > 0)
     638           0 :             ctx->cursor_column -= 1;
     639           0 :         ctx->cursor_charset = CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH;
     640           0 :         break;
     641        1594 :       default:
     642        1594 :         ctx->cursor_charset = CCSET_BASIC_AMERICAN;
     643        1594 :         write_char(ctx, screen, hi);
     644        1594 :         break;
     645             :     }
     646             : 
     647        1594 :     if (lo) {
     648        1480 :         write_char(ctx, screen, lo);
     649             :     }
     650        1594 :     write_char(ctx, screen, 0);
     651             : 
     652        1594 :     if (ctx->mode != CCMODE_POPON)
     653         113 :         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        1594 : }
     660             : 
     661        2757 : static void process_cc608(CCaptionSubContext *ctx, int64_t pts, uint8_t hi, uint8_t lo)
     662             : {
     663        2757 :     if (hi == ctx->prev_cmd[0] && lo == ctx->prev_cmd[1]) {
     664             :         /* ignore redundant command */
     665         648 :         return;
     666             :     }
     667             : 
     668             :     /* set prev command */
     669        2109 :     ctx->prev_cmd[0] = hi;
     670        2109 :     ctx->prev_cmd[1] = lo;
     671             : 
     672        2109 :     if ( (hi == 0x10 && (lo >= 0x40 && lo <= 0x5f)) ||
     673        2109 :        ( (hi >= 0x11 && hi <= 0x17) && (lo >= 0x40 && lo <= 0x7f) ) ) {
     674         197 :         handle_pac(ctx, hi, lo);
     675        1912 :     } else if ( ( hi == 0x11 && lo >= 0x20 && lo <= 0x2f ) ||
     676           0 :                 ( hi == 0x17 && lo >= 0x2e && lo <= 0x2f) ) {
     677          11 :         handle_textattr(ctx, hi, lo);
     678        1901 :     } else if (hi == 0x14 || hi == 0x15 || hi == 0x1c) {
     679         307 :         switch (lo) {
     680          92 :         case 0x20:
     681             :             /* resume caption loading */
     682          92 :             ctx->mode = CCMODE_POPON;
     683          92 :             break;
     684           0 :         case 0x24:
     685           0 :             handle_delete_end_of_row(ctx, hi, lo);
     686           0 :             break;
     687           9 :         case 0x25:
     688             :         case 0x26:
     689             :         case 0x27:
     690           9 :             ctx->rollup = lo - 0x23;
     691           9 :             ctx->mode = CCMODE_ROLLUP;
     692           9 :             break;
     693           0 :         case 0x29:
     694             :             /* resume direct captioning */
     695           0 :             ctx->mode = CCMODE_PAINTON;
     696           0 :             break;
     697           0 :         case 0x2b:
     698             :             /* resume text display */
     699           0 :             ctx->mode = CCMODE_TEXT;
     700           0 :             break;
     701         104 :         case 0x2c:
     702             :             /* erase display memory */
     703         104 :             handle_edm(ctx, pts);
     704         104 :             break;
     705           9 :         case 0x2d:
     706             :             /* carriage return */
     707             :             ff_dlog(ctx, "carriage return\n");
     708           9 :             if (!ctx->real_time)
     709           6 :                 reap_screen(ctx, pts);
     710           9 :             roll_up(ctx);
     711           9 :             ctx->cursor_column = 0;
     712           9 :             break;
     713           0 :         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           0 :             if (ctx->real_time) {
     718           0 :                 struct Screen *screen = ctx->screen + !ctx->active_screen;
     719           0 :                 screen->row_used = 0;
     720             :             }
     721           0 :             break;
     722          93 :         case 0x2f:
     723             :             /* end of caption */
     724             :             ff_dlog(ctx, "handle_eoc\n");
     725          93 :             handle_eoc(ctx, pts);
     726          93 :             break;
     727           0 :         default:
     728             :             ff_dlog(ctx, "Unknown command 0x%hhx 0x%hhx\n", hi, lo);
     729           0 :             break;
     730             :         }
     731         307 :     } else if (hi >= 0x11 && hi <= 0x13) {
     732             :         /* Special characters */
     733           0 :         handle_char(ctx, hi, lo, pts);
     734        1594 :     } else if (hi >= 0x20) {
     735             :         /* Standard characters (always in pairs) */
     736        1594 :         handle_char(ctx, hi, lo, pts);
     737        1594 :         ctx->prev_cmd[0] = ctx->prev_cmd[1] = 0;
     738           0 :     } else if (hi == 0x17 && lo >= 0x21 && lo <= 0x23) {
     739             :         int i;
     740             :         /* Tab offsets (spacing) */
     741           0 :         for (i = 0; i < lo - 0x20; i++) {
     742           0 :             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         576 : static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avpkt)
     751             : {
     752         576 :     CCaptionSubContext *ctx = avctx->priv_data;
     753         576 :     AVSubtitle *sub = data;
     754         576 :     const int64_t start_time = sub->pts;
     755         576 :     uint8_t *bptr = NULL;
     756         576 :     int len = avpkt->size;
     757         576 :     int ret = 0;
     758             :     int i;
     759             : 
     760         576 :     av_fast_padded_malloc(&ctx->pktbuf, &ctx->pktbuf_size, len);
     761         576 :     if (!ctx->pktbuf) {
     762           0 :         av_log(ctx, AV_LOG_WARNING, "Insufficient Memory of %d truncated to %d\n", len, ctx->pktbuf_size);
     763           0 :         return AVERROR(ENOMEM);
     764             :     }
     765         576 :     memcpy(ctx->pktbuf, avpkt->data, len);
     766         576 :     bptr = ctx->pktbuf;
     767             : 
     768        8829 :     for (i  = 0; i < len; i += 3) {
     769        8253 :         uint8_t cc_type = *(bptr + i) & 3;
     770        8253 :         if (validate_cc_data_pair(bptr + i))
     771        5294 :             continue;
     772             :         /* ignoring data field 1 */
     773        2959 :         if(cc_type == 1)
     774         202 :             continue;
     775             :         else
     776        2757 :             process_cc608(ctx, start_time, *(bptr + i + 1) & 0x7f, *(bptr + i + 2) & 0x7f);
     777             : 
     778        2757 :         if (!ctx->buffer_changed)
     779        2554 :             continue;
     780         203 :         ctx->buffer_changed = 0;
     781             : 
     782         203 :         if (*ctx->buffer.str || ctx->real_time)
     783             :         {
     784             :             ff_dlog(ctx, "cdp writing data (%s)\n",ctx->buffer.str);
     785          97 :             ret = ff_ass_add_rect(sub, ctx->buffer.str, ctx->readorder++, 0, NULL, NULL);
     786          97 :             if (ret < 0)
     787           0 :                 return ret;
     788          97 :             sub->pts = ctx->start_time;
     789          97 :             if (!ctx->real_time)
     790          97 :                 sub->end_display_time = av_rescale_q(ctx->end_time - ctx->start_time,
     791          97 :                                                      AV_TIME_BASE_Q, ms_tb);
     792             :             else
     793           0 :                 sub->end_display_time = -1;
     794          97 :             ctx->buffer_changed = 0;
     795          97 :             ctx->last_real_time = sub->pts;
     796          97 :             ctx->screen_touched = 0;
     797             :         }
     798             :     }
     799             : 
     800         607 :     if (ctx->real_time && ctx->screen_touched &&
     801          31 :         sub->pts > ctx->last_real_time + av_rescale_q(200, ms_tb, AV_TIME_BASE_Q)) {
     802          30 :         ctx->last_real_time = sub->pts;
     803          30 :         ctx->screen_touched = 0;
     804             : 
     805          30 :         capture_screen(ctx);
     806          30 :         ctx->buffer_changed = 0;
     807             : 
     808          30 :         ret = ff_ass_add_rect(sub, ctx->buffer.str, ctx->readorder++, 0, NULL, NULL);
     809          30 :         if (ret < 0)
     810           0 :             return ret;
     811          30 :         sub->end_display_time = -1;
     812             :     }
     813             : 
     814         576 :     *got_sub = sub->num_rects > 0;
     815         576 :     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             : 
     832             : AVCodec ff_ccaption_decoder = {
     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             : };

Generated by: LCOV version 1.13