ccaption_dec.c 25.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * Closed Caption Decoding
 * Copyright (c) 2015 Anshul Maheshwari
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "avcodec.h"
#include "ass.h"
#include "libavutil/opt.h"

#define SCREEN_ROWS 15
#define SCREEN_COLUMNS 32

29 30 31
#define SET_FLAG(var, val)   ( (var) |=   ( 1 << (val)) )
#define UNSET_FLAG(var, val) ( (var) &=  ~( 1 << (val)) )
#define CHECK_FLAG(var, val) ( (var) &    ( 1 << (val)) )
32

33
static const AVRational ms_tb = {1, 1000};
34

35 36 37 38 39 40 41
/*
 * TODO list
 * 1) handle font and color completely
 */
enum cc_mode {
    CCMODE_POPON,
    CCMODE_PAINTON,
42
    CCMODE_ROLLUP,
43 44 45
    CCMODE_TEXT,
};

46
enum cc_color_code {
47 48 49 50 51 52 53 54 55
    CCCOL_WHITE,
    CCCOL_GREEN,
    CCCOL_BLUE,
    CCCOL_CYAN,
    CCCOL_RED,
    CCCOL_YELLOW,
    CCCOL_MAGENTA,
    CCCOL_USERDEFINED,
    CCCOL_BLACK,
56
    CCCOL_TRANSPARENT,
57 58
};

59
enum cc_font {
60 61 62
    CCFONT_REGULAR,
    CCFONT_ITALICS,
    CCFONT_UNDERLINED,
63
    CCFONT_UNDERLINED_ITALICS,
64 65
};

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
enum cc_charset {
    CCSET_BASIC_AMERICAN,
    CCSET_SPECIAL_AMERICAN,
    CCSET_EXTENDED_SPANISH_FRENCH_MISC,
    CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH,
};

static const char *charset_overrides[4][128] =
{
    [CCSET_BASIC_AMERICAN] = {
        [0x27] = "\u2019",
        [0x2a] = "\u00e1",
        [0x5c] = "\u00e9",
        [0x5e] = "\u00ed",
        [0x5f] = "\u00f3",
        [0x60] = "\u00fa",
        [0x7b] = "\u00e7",
        [0x7c] = "\u00f7",
        [0x7d] = "\u00d1",
        [0x7e] = "\u00f1",
        [0x7f] = "\u2588"
    },
    [CCSET_SPECIAL_AMERICAN] = {
        [0x30] = "\u00ae",
        [0x31] = "\u00b0",
        [0x32] = "\u00bd",
        [0x33] = "\u00bf",
        [0x34] = "\u2122",
        [0x35] = "\u00a2",
        [0x36] = "\u00a3",
        [0x37] = "\u266a",
        [0x38] = "\u00e0",
        [0x39] = "\u00A0",
        [0x3a] = "\u00e8",
        [0x3b] = "\u00e2",
        [0x3c] = "\u00ea",
        [0x3d] = "\u00ee",
        [0x3e] = "\u00f4",
        [0x3f] = "\u00fb",
    },
    [CCSET_EXTENDED_SPANISH_FRENCH_MISC] = {
        [0x20] = "\u00c1",
        [0x21] = "\u00c9",
        [0x22] = "\u00d3",
        [0x23] = "\u00da",
        [0x24] = "\u00dc",
        [0x25] = "\u00fc",
        [0x26] = "\u00b4",
        [0x27] = "\u00a1",
        [0x28] = "*",
        [0x29] = "\u2018",
        [0x2a] = "-",
        [0x2b] = "\u00a9",
        [0x2c] = "\u2120",
        [0x2d] = "\u00b7",
        [0x2e] = "\u201c",
        [0x2f] = "\u201d",
        [0x30] = "\u00c0",
        [0x31] = "\u00c2",
        [0x32] = "\u00c7",
        [0x33] = "\u00c8",
        [0x34] = "\u00ca",
        [0x35] = "\u00cb",
        [0x36] = "\u00eb",
        [0x37] = "\u00ce",
        [0x38] = "\u00cf",
        [0x39] = "\u00ef",
        [0x3a] = "\u00d4",
        [0x3b] = "\u00d9",
        [0x3c] = "\u00f9",
        [0x3d] = "\u00db",
        [0x3e] = "\u00ab",
        [0x3f] = "\u00bb",
    },
    [CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH] = {
        [0x20] = "\u00c3",
        [0x21] = "\u00e3",
        [0x22] = "\u00cd",
        [0x23] = "\u00cc",
        [0x24] = "\u00ec",
        [0x25] = "\u00d2",
        [0x26] = "\u00f2",
        [0x27] = "\u00d5",
        [0x28] = "\u00f5",
        [0x29] = "{",
        [0x2a] = "}",
        [0x2b] = "\\",
        [0x2c] = "^",
        [0x2d] = "_",
        [0x2e] = "|",
        [0x2f] = "~",
        [0x30] = "\u00c4",
        [0x31] = "\u00e4",
        [0x32] = "\u00d6",
        [0x33] = "\u00f6",
        [0x34] = "\u00df",
        [0x35] = "\u00a5",
        [0x36] = "\u00a4",
        [0x37] = "\u00a6",
        [0x38] = "\u00c5",
        [0x39] = "\u00e5",
        [0x3a] = "\u00d8",
        [0x3b] = "\u00f8",
        [0x3c] = "\u250c",
        [0x3d] = "\u2510",
        [0x3e] = "\u2514",
        [0x3f] = "\u2518",
    },
};

176
static const unsigned char pac2_attribs[32][3] = // Color, font, ident
177
{
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
    { CCCOL_WHITE,   CCFONT_REGULAR,            0 },  // 0x40 || 0x60
    { CCCOL_WHITE,   CCFONT_UNDERLINED,         0 },  // 0x41 || 0x61
    { CCCOL_GREEN,   CCFONT_REGULAR,            0 },  // 0x42 || 0x62
    { CCCOL_GREEN,   CCFONT_UNDERLINED,         0 },  // 0x43 || 0x63
    { CCCOL_BLUE,    CCFONT_REGULAR,            0 },  // 0x44 || 0x64
    { CCCOL_BLUE,    CCFONT_UNDERLINED,         0 },  // 0x45 || 0x65
    { CCCOL_CYAN,    CCFONT_REGULAR,            0 },  // 0x46 || 0x66
    { CCCOL_CYAN,    CCFONT_UNDERLINED,         0 },  // 0x47 || 0x67
    { CCCOL_RED,     CCFONT_REGULAR,            0 },  // 0x48 || 0x68
    { CCCOL_RED,     CCFONT_UNDERLINED,         0 },  // 0x49 || 0x69
    { CCCOL_YELLOW,  CCFONT_REGULAR,            0 },  // 0x4a || 0x6a
    { CCCOL_YELLOW,  CCFONT_UNDERLINED,         0 },  // 0x4b || 0x6b
    { CCCOL_MAGENTA, CCFONT_REGULAR,            0 },  // 0x4c || 0x6c
    { CCCOL_MAGENTA, CCFONT_UNDERLINED,         0 },  // 0x4d || 0x6d
    { CCCOL_WHITE,   CCFONT_ITALICS,            0 },  // 0x4e || 0x6e
    { CCCOL_WHITE,   CCFONT_UNDERLINED_ITALICS, 0 },  // 0x4f || 0x6f
    { CCCOL_WHITE,   CCFONT_REGULAR,            0 },  // 0x50 || 0x70
    { CCCOL_WHITE,   CCFONT_UNDERLINED,         0 },  // 0x51 || 0x71
    { CCCOL_WHITE,   CCFONT_REGULAR,            4 },  // 0x52 || 0x72
    { CCCOL_WHITE,   CCFONT_UNDERLINED,         4 },  // 0x53 || 0x73
    { CCCOL_WHITE,   CCFONT_REGULAR,            8 },  // 0x54 || 0x74
    { CCCOL_WHITE,   CCFONT_UNDERLINED,         8 },  // 0x55 || 0x75
    { CCCOL_WHITE,   CCFONT_REGULAR,           12 },  // 0x56 || 0x76
    { CCCOL_WHITE,   CCFONT_UNDERLINED,        12 },  // 0x57 || 0x77
    { CCCOL_WHITE,   CCFONT_REGULAR,           16 },  // 0x58 || 0x78
    { CCCOL_WHITE,   CCFONT_UNDERLINED,        16 },  // 0x59 || 0x79
    { CCCOL_WHITE,   CCFONT_REGULAR,           20 },  // 0x5a || 0x7a
    { CCCOL_WHITE,   CCFONT_UNDERLINED,        20 },  // 0x5b || 0x7b
    { CCCOL_WHITE,   CCFONT_REGULAR,           24 },  // 0x5c || 0x7c
    { CCCOL_WHITE,   CCFONT_UNDERLINED,        24 },  // 0x5d || 0x7d
    { CCCOL_WHITE,   CCFONT_REGULAR,           28 },  // 0x5e || 0x7e
    { CCCOL_WHITE,   CCFONT_UNDERLINED,        28 }   // 0x5f || 0x7f
210
    /* total 32 entries */
211
};
212

213 214
struct Screen {
    /* +1 is used to compensate null character of string */
215 216 217 218
    uint8_t characters[SCREEN_ROWS+1][SCREEN_COLUMNS+1];
    uint8_t charsets[SCREEN_ROWS+1][SCREEN_COLUMNS+1];
    uint8_t colors[SCREEN_ROWS+1][SCREEN_COLUMNS+1];
    uint8_t fonts[SCREEN_ROWS+1][SCREEN_COLUMNS+1];
219 220 221
    /*
     * Bitmask of used rows; if a bit is not set, the
     * corresponding row is not used.
222
     * for setting row 1  use row | (1 << 0)
223 224
     * for setting row 15 use row | (1 << 14)
     */
225
    int16_t row_used;
226 227 228 229
};

typedef struct CCaptionSubContext {
    AVClass *class;
230
    int real_time;
231 232 233 234 235 236
    struct Screen screen[2];
    int active_screen;
    uint8_t cursor_row;
    uint8_t cursor_column;
    uint8_t cursor_color;
    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 243 244 245
    int64_t start_time;
    /* visible screen time */
    int64_t startv_time;
    int64_t end_time;
246 247
    int screen_touched;
    int64_t last_real_time;
248 249
    char prev_cmd[2];
    /* buffer to store pkt data */
250 251
    uint8_t *pktbuf;
    int pktbuf_size;
252
    int readorder;
253
} CCaptionSubContext;
254 255 256 257 258 259 260 261 262


static av_cold int init_decoder(AVCodecContext *avctx)
{
    int ret;
    CCaptionSubContext *ctx = avctx->priv_data;

    av_bprint_init(&ctx->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
    /* taking by default roll up to 2 */
263
    ctx->mode = CCMODE_ROLLUP;
264
    ctx->rollup = 2;
265
    ctx->cursor_row = 10;
266 267 268 269 270 271 272 273 274
    ret = ff_ass_subtitle_header(avctx, "Monospace",
                                 ASS_DEFAULT_FONT_SIZE,
                                 ASS_DEFAULT_COLOR,
                                 ASS_DEFAULT_BACK_COLOR,
                                 ASS_DEFAULT_BOLD,
                                 ASS_DEFAULT_ITALIC,
                                 ASS_DEFAULT_UNDERLINE,
                                 3,
                                 ASS_DEFAULT_ALIGNMENT);
275
    if (ret < 0) {
276
        return ret;
277
    }
278

279 280 281 282 283 284
    return ret;
}

static av_cold int close_decoder(AVCodecContext *avctx)
{
    CCaptionSubContext *ctx = avctx->priv_data;
285
    av_bprint_finalize(&ctx->buffer, NULL);
286 287
    av_freep(&ctx->pktbuf);
    ctx->pktbuf_size = 0;
288 289
    return 0;
}
290

291 292 293 294 295 296 297 298 299
static void flush_decoder(AVCodecContext *avctx)
{
    CCaptionSubContext *ctx = avctx->priv_data;
    ctx->screen[0].row_used = 0;
    ctx->screen[1].row_used = 0;
    ctx->prev_cmd[0] = 0;
    ctx->prev_cmd[1] = 0;
    ctx->mode = CCMODE_ROLLUP;
    ctx->rollup = 2;
300
    ctx->cursor_row = 10;
301 302 303
    ctx->cursor_column = 0;
    ctx->cursor_font = 0;
    ctx->cursor_color = 0;
304
    ctx->cursor_charset = 0;
305
    ctx->active_screen = 0;
306 307
    ctx->last_real_time = 0;
    ctx->screen_touched = 0;
308
    ctx->buffer_changed = 0;
309 310
    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
        ctx->readorder = 0;
311 312 313
    av_bprint_clear(&ctx->buffer);
}

314 315 316
/**
 * @param ctx closed caption context just to print log
 */
317
static void write_char(CCaptionSubContext *ctx, struct Screen *screen, char ch)
318
{
319 320
    uint8_t col = ctx->cursor_column;
    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 328
        charset[col] = ctx->cursor_charset;
        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 345 346 347 348 349
/**
 * This function after validating parity bit, also remove it from data pair.
 * The first byte doesn't pass parity, we replace it with a solid blank
 * and process the pair.
 * If the second byte doesn't pass parity, it returns INVALIDDATA
 * user can ignore the whole pair and pass the other pair.
 */
350
static int validate_cc_data_pair(uint8_t *cc_data_pair)
351 352 353 354 355 356 357 358 359
{
    uint8_t cc_valid = (*cc_data_pair & 4) >>2;
    uint8_t cc_type = *cc_data_pair & 3;

    if (!cc_valid)
        return AVERROR_INVALIDDATA;

    // if EIA-608 data then verify parity.
    if (cc_type==0 || cc_type==1) {
360
        if (!av_parity(cc_data_pair[2])) {
361 362
            return AVERROR_INVALIDDATA;
        }
363
        if (!av_parity(cc_data_pair[1])) {
364 365 366 367 368
            cc_data_pair[1]=0x7F;
        }
    }

    //Skip non-data
369
    if ((cc_data_pair[0] == 0xFA || cc_data_pair[0] == 0xFC || cc_data_pair[0] == 0xFD)
370 371 372 373
         && (cc_data_pair[1] & 0x7F) == 0 && (cc_data_pair[2] & 0x7F) == 0)
        return AVERROR_PATCHWELCOME;

    //skip 708 data
374
    if (cc_type == 3 || cc_type == 2)
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
        return AVERROR_PATCHWELCOME;

    /* remove parity bit */
    cc_data_pair[1] &= 0x7F;
    cc_data_pair[2] &= 0x7F;

    return 0;
}

static struct Screen *get_writing_screen(CCaptionSubContext *ctx)
{
    switch (ctx->mode) {
    case CCMODE_POPON:
        // use Inactive screen
        return ctx->screen + !ctx->active_screen;
    case CCMODE_PAINTON:
391
    case CCMODE_ROLLUP:
392 393 394 395 396 397 398 399
    case CCMODE_TEXT:
        // use active screen
        return ctx->screen + ctx->active_screen;
    }
    /* It was never an option */
    return NULL;
}

400 401 402 403 404
static void roll_up(CCaptionSubContext *ctx)
{
    struct Screen *screen;
    int i, keep_lines;

405
    if (ctx->mode == CCMODE_TEXT)
406 407 408 409 410 411 412 413 414
        return;

    screen = get_writing_screen(ctx);

    /* +1 signify cursor_row starts from 0
     * Can't keep lines less then row cursor pos
     */
    keep_lines = FFMIN(ctx->cursor_row + 1, ctx->rollup);

415 416 417
    for (i = 0; i < SCREEN_ROWS; i++) {
        if (i > ctx->cursor_row - keep_lines && i <= ctx->cursor_row)
            continue;
418
        UNSET_FLAG(screen->row_used, i);
419
    }
420

421
    for (i = 0; i < keep_lines && screen->row_used; i++) {
422 423
        const int i_row = ctx->cursor_row - keep_lines + i + 1;

424 425 426
        memcpy(screen->characters[i_row], screen->characters[i_row+1], SCREEN_COLUMNS);
        memcpy(screen->colors[i_row], screen->colors[i_row+1], SCREEN_COLUMNS);
        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 430
            SET_FLAG(screen->row_used, i_row);
    }
431

432 433 434
    UNSET_FLAG(screen->row_used, ctx->cursor_row);
}

435
static int capture_screen(CCaptionSubContext *ctx)
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 443 444 445 446 447 448 449 450 451 452 453 454
    for (i = 0; screen->row_used && i < SCREEN_ROWS; i++)
    {
        if (CHECK_FLAG(screen->row_used, i)) {
            const char *row = screen->characters[i];
            const char *charset = screen->charsets[i];
            j = 0;
            while (row[j] == ' ' && charset[j] == CCSET_BASIC_AMERICAN)
                j++;
            if (!tab || j < tab)
                tab = j;
        }
    }

455
    for (i = 0; screen->row_used && i < SCREEN_ROWS; i++)
456
    {
457
        if (CHECK_FLAG(screen->row_used, i)) {
458 459
            const char *row = screen->characters[i];
            const char *font = screen->fonts[i];
460 461
            const char *charset = screen->charsets[i];
            const char *override;
462 463
            int x, y, seen_char = 0;
            j = 0;
464 465

            /* skip leading space */
466
            while (row[j] == ' ' && charset[j] == CCSET_BASIC_AMERICAN && j < tab)
467 468
                j++;

469 470 471 472
            x = ASS_DEFAULT_PLAYRESX * (0.1 + 0.0250 * j);
            y = ASS_DEFAULT_PLAYRESY * (0.1 + 0.0533 * i);
            av_bprintf(&ctx->buffer, "{\\an7}{\\pos(%d,%d)}", x, y);

473
            for (; j < SCREEN_COLUMNS; j++) {
474 475
                const char *e_tag = "", *s_tag = "";

476 477 478 479 480 481
                if (row[j] == 0)
                    break;

                if (prev_font != font[j]) {
                    switch (prev_font) {
                    case CCFONT_ITALICS:
482
                        e_tag = "{\\i0}";
483 484
                        break;
                    case CCFONT_UNDERLINED:
485
                        e_tag = "{\\u0}";
486 487
                        break;
                    case CCFONT_UNDERLINED_ITALICS:
488
                        e_tag = "{\\u0}{\\i0}";
489 490 491 492
                        break;
                    }
                    switch (font[j]) {
                    case CCFONT_ITALICS:
493
                        s_tag = "{\\i1}";
494 495
                        break;
                    case CCFONT_UNDERLINED:
496
                        s_tag = "{\\u1}";
497 498
                        break;
                    case CCFONT_UNDERLINED_ITALICS:
499
                        s_tag = "{\\u1}{\\i1}";
500 501 502 503
                        break;
                    }
                }
                prev_font = font[j];
504 505 506
                override = charset_overrides[(int)charset[j]][(int)row[j]];
                if (override) {
                    av_bprintf(&ctx->buffer, "%s%s%s", e_tag, s_tag, override);
507 508 509
                    seen_char = 1;
                } else if (row[j] == ' ' && !seen_char) {
                    av_bprintf(&ctx->buffer, "%s%s\\h", e_tag, s_tag);
510 511
                } else {
                    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 520
    if (!av_bprint_is_complete(&ctx->buffer))
        return AVERROR(ENOMEM);
521
    if (screen->row_used && ctx->buffer.len >= 2) {
522 523 524
        ctx->buffer.len -= 2;
        ctx->buffer.str[ctx->buffer.len] = 0;
    }
525
    ctx->buffer_changed = 1;
526
    return 0;
527 528 529 530 531
}

static int reap_screen(CCaptionSubContext *ctx, int64_t pts)
{
    ctx->start_time = ctx->startv_time;
532 533
    ctx->startv_time = pts;
    ctx->end_time = pts;
534
    return capture_screen(ctx);
535 536
}

537
static void handle_textattr(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo)
538 539 540 541
{
    int i = lo - 0x20;
    struct Screen *screen = get_writing_screen(ctx);

542
    if (i >= 32)
543 544
        return;

545
    ctx->cursor_color = pac2_attribs[i][0];
546 547
    ctx->cursor_font = pac2_attribs[i][1];

548
    SET_FLAG(screen->row_used, ctx->cursor_row);
549
    write_char(ctx, screen, ' ');
550
}
551

552
static void handle_pac(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo)
553 554 555 556 557 558
{
    static const int8_t row_map[] = {
        11, -1, 1, 2, 3, 4, 12, 13, 14, 15, 5, 6, 7, 8, 9, 10
    };
    const int index = ( (hi<<1) & 0x0e) | ( (lo>>5) & 0x01 );
    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 567 568 569 570

    lo &= 0x1f;

    ctx->cursor_row = row_map[index] - 1;
    ctx->cursor_color =  pac2_attribs[lo][0];
    ctx->cursor_font = pac2_attribs[lo][1];
571
    ctx->cursor_charset = CCSET_BASIC_AMERICAN;
572 573
    ctx->cursor_column = 0;
    indent = pac2_attribs[lo][2];
574
    for (i = 0; i < indent; i++) {
575
        write_char(ctx, screen, ' ');
576 577 578
    }
}

579 580 581 582
/**
 * @param pts it is required to set end time
 */
static void handle_edm(CCaptionSubContext *ctx, int64_t pts)
583
{
584
    struct Screen *screen = ctx->screen + ctx->active_screen;
585

586 587 588 589 590
    // In buffered mode, keep writing to screen until it is wiped.
    // Before wiping the display, capture contents to emit subtitle.
    if (!ctx->real_time)
        reap_screen(ctx, pts);

591
    screen->row_used = 0;
592 593 594 595 596

    // In realtime mode, emit an empty caption so the last one doesn't
    // stay on the screen.
    if (ctx->real_time)
        reap_screen(ctx, pts);
597
}
598

599
static void handle_eoc(CCaptionSubContext *ctx, int64_t pts)
600
{
601 602 603 604 605
    // In buffered mode, we wait til the *next* EOC and
    // reap what was already on the screen since the last EOC.
    if (!ctx->real_time)
        handle_edm(ctx,pts);

606 607
    ctx->active_screen = !ctx->active_screen;
    ctx->cursor_column = 0;
608 609 610 611 612

    // In realtime mode, we display the buffered contents (after
    // flipping the buffer to active above) as soon as EOC arrives.
    if (ctx->real_time)
        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 622 623 624
static void handle_char(CCaptionSubContext *ctx, char hi, char lo, int64_t pts)
{
    struct Screen *screen = get_writing_screen(ctx);

625
    SET_FLAG(screen->row_used, ctx->cursor_row);
626

627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
    switch (hi) {
      case 0x11:
        ctx->cursor_charset = CCSET_SPECIAL_AMERICAN;
        break;
      case 0x12:
        if (ctx->cursor_column > 0)
            ctx->cursor_column -= 1;
        ctx->cursor_charset = CCSET_EXTENDED_SPANISH_FRENCH_MISC;
        break;
      case 0x13:
        if (ctx->cursor_column > 0)
            ctx->cursor_column -= 1;
        ctx->cursor_charset = CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH;
        break;
      default:
        ctx->cursor_charset = CCSET_BASIC_AMERICAN;
        write_char(ctx, screen, hi);
        break;
    }
646

647
    if (lo) {
648
        write_char(ctx, screen, lo);
649
    }
650
    write_char(ctx, screen, 0);
651

652 653 654
    if (ctx->mode != CCMODE_POPON)
        ctx->screen_touched = 1;

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 666 667 668 669 670 671 672 673
        return;
    }

    /* set prev command */
    ctx->prev_cmd[0] = hi;
    ctx->prev_cmd[1] = lo;

    if ( (hi == 0x10 && (lo >= 0x40 && lo <= 0x5f)) ||
       ( (hi >= 0x11 && hi <= 0x17) && (lo >= 0x40 && lo <= 0x7f) ) ) {
674 675 676 677
        handle_pac(ctx, hi, lo);
    } else if ( ( hi == 0x11 && lo >= 0x20 && lo <= 0x2f ) ||
                ( hi == 0x17 && lo >= 0x2e && lo <= 0x2f) ) {
        handle_textattr(ctx, hi, lo);
678 679 680 681 682 683 684 685 686 687 688 689
    } else if (hi == 0x14 || hi == 0x15 || hi == 0x1c) {
        switch (lo) {
        case 0x20:
            /* resume caption loading */
            ctx->mode = CCMODE_POPON;
            break;
        case 0x24:
            handle_delete_end_of_row(ctx, hi, lo);
            break;
        case 0x25:
        case 0x26:
        case 0x27:
690
            ctx->rollup = lo - 0x23;
691
            ctx->mode = CCMODE_ROLLUP;
692 693 694 695 696 697 698 699 700 701 702
            break;
        case 0x29:
            /* resume direct captioning */
            ctx->mode = CCMODE_PAINTON;
            break;
        case 0x2b:
            /* resume text display */
            ctx->mode = CCMODE_TEXT;
            break;
        case 0x2c:
            /* erase display memory */
703
            handle_edm(ctx, pts);
704 705 706 707
            break;
        case 0x2d:
            /* carriage return */
            ff_dlog(ctx, "carriage return\n");
708 709
            if (!ctx->real_time)
                reap_screen(ctx, pts);
710 711 712
            roll_up(ctx);
            ctx->cursor_column = 0;
            break;
713 714 715 716 717 718 719 720 721
        case 0x2e:
            /* erase buffered (non displayed) memory */
            // Only in realtime mode. In buffered mode, we re-use the inactive screen
            // for our own buffering.
            if (ctx->real_time) {
                struct Screen *screen = ctx->screen + !ctx->active_screen;
                screen->row_used = 0;
            }
            break;
722 723 724
        case 0x2f:
            /* end of caption */
            ff_dlog(ctx, "handle_eoc\n");
725
            handle_eoc(ctx, pts);
726 727 728 729 730
            break;
        default:
            ff_dlog(ctx, "Unknown command 0x%hhx 0x%hhx\n", hi, lo);
            break;
        }
731 732 733
    } else if (hi >= 0x11 && hi <= 0x13) {
        /* Special characters */
        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 743
            handle_char(ctx, ' ', 0, pts);
        }
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 751 752 753
static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avpkt)
{
    CCaptionSubContext *ctx = avctx->priv_data;
    AVSubtitle *sub = data;
754
    const int64_t start_time = sub->pts;
755 756 757 758 759
    uint8_t *bptr = NULL;
    int len = avpkt->size;
    int ret = 0;
    int i;

760 761 762 763
    av_fast_padded_malloc(&ctx->pktbuf, &ctx->pktbuf_size, len);
    if (!ctx->pktbuf) {
        av_log(ctx, AV_LOG_WARNING, "Insufficient Memory of %d truncated to %d\n", len, ctx->pktbuf_size);
        return AVERROR(ENOMEM);
764
    }
765 766
    memcpy(ctx->pktbuf, avpkt->data, len);
    bptr = ctx->pktbuf;
767 768 769

    for (i  = 0; i < len; i += 3) {
        uint8_t cc_type = *(bptr + i) & 3;
770
        if (validate_cc_data_pair(bptr + i))
771 772 773 774 775
            continue;
        /* ignoring data field 1 */
        if(cc_type == 1)
            continue;
        else
776
            process_cc608(ctx, start_time, *(bptr + i + 1) & 0x7f, *(bptr + i + 2) & 0x7f);
777 778 779 780 781 782

        if (!ctx->buffer_changed)
            continue;
        ctx->buffer_changed = 0;

        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 787
            if (ret < 0)
                return ret;
788
            sub->pts = ctx->start_time;
789 790
            if (!ctx->real_time)
                sub->end_display_time = av_rescale_q(ctx->end_time - ctx->start_time,
791
                                                     AV_TIME_BASE_Q, ms_tb);
792 793
            else
                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 802
        sub->pts > ctx->last_real_time + av_rescale_q(200, ms_tb, AV_TIME_BASE_Q)) {
        ctx->last_real_time = sub->pts;
803 804 805 806 807
        ctx->screen_touched = 0;

        capture_screen(ctx);
        ctx->buffer_changed = 0;

808
        ret = ff_ass_add_rect(sub, ctx->buffer.str, ctx->readorder++, 0, NULL, NULL);
809 810
        if (ret < 0)
            return ret;
811
        sub->end_display_time = -1;
812 813
    }

814 815 816
    *got_sub = sub->num_rects > 0;
    return ret;
}
817

818 819
#define OFFSET(x) offsetof(CCaptionSubContext, x)
#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 823
    {NULL}
};
824

825 826 827 828 829 830 831 832 833
static const AVClass ccaption_dec_class = {
    .class_name = "Closed caption Decoder",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

AVCodec ff_ccaption_decoder = {
    .name           = "cc_dec",
834
    .long_name      = NULL_IF_CONFIG_SMALL("Closed Caption (EIA-608 / CEA-708)"),
835 836 837 838 839
    .type           = AVMEDIA_TYPE_SUBTITLE,
    .id             = AV_CODEC_ID_EIA_608,
    .priv_data_size = sizeof(CCaptionSubContext),
    .init           = init_decoder,
    .close          = close_decoder,
840
    .flush          = flush_decoder,
841 842 843
    .decode         = decode,
    .priv_class     = &ccaption_dec_class,
};