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