970 lines · 33.8 KB · C
RawDownload
/* * 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 <assert.h>#include "avcodec.h"#include "ass.h"#include "codec_internal.h"#include "libavutil/opt.h" #define SCREEN_ROWS 15#define SCREEN_COLUMNS 32 #define SET_FLAG(var, val)   ( (var) |=   ( 1 << (val)) )#define UNSET_FLAG(var, val) ( (var) &=  ~( 1 << (val)) )#define CHECK_FLAG(var, val) ( (var) &    ( 1 << (val)) ) static const AVRational ms_tb = {1, 1000}; enum cc_mode {    CCMODE_POPON,    CCMODE_PAINTON,    CCMODE_ROLLUP,    CCMODE_TEXT,}; enum cc_color_code {    CCCOL_WHITE,    CCCOL_GREEN,    CCCOL_BLUE,    CCCOL_CYAN,    CCCOL_RED,    CCCOL_YELLOW,    CCCOL_MAGENTA,    CCCOL_USERDEFINED,    CCCOL_BLACK,    CCCOL_TRANSPARENT,}; enum cc_font {    CCFONT_REGULAR,    CCFONT_ITALICS,    CCFONT_UNDERLINED,    CCFONT_UNDERLINED_ITALICS,}; enum cc_charset {    CCSET_BASIC_AMERICAN,    CCSET_SPECIAL_AMERICAN,    CCSET_EXTENDED_SPANISH_FRENCH_MISC,    CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH,}; #define CHARSET_OVERRIDE_LIST(START_SET, ENTRY, END_SET) \    START_SET(CCSET_BASIC_AMERICAN)                      \        ENTRY(0x27, "\u2019")                            \        ENTRY(0x2a, "\u00e1")                            \        ENTRY(0x5c, "\u00e9")                            \        ENTRY(0x5e, "\u00ed")                            \        ENTRY(0x5f, "\u00f3")                            \        ENTRY(0x60, "\u00fa")                            \        ENTRY(0x7b, "\u00e7")                            \        ENTRY(0x7c, "\u00f7")                            \        ENTRY(0x7d, "\u00d1")                            \        ENTRY(0x7e, "\u00f1")                            \        ENTRY(0x7f, "\u2588")                            \    END_SET                                              \    START_SET(CCSET_SPECIAL_AMERICAN)                    \        ENTRY(0x30, "\u00ae")                            \        ENTRY(0x31, "\u00b0")                            \        ENTRY(0x32, "\u00bd")                            \        ENTRY(0x33, "\u00bf")                            \        ENTRY(0x34, "\u2122")                            \        ENTRY(0x35, "\u00a2")                            \        ENTRY(0x36, "\u00a3")                            \        ENTRY(0x37, "\u266a")                            \        ENTRY(0x38, "\u00e0")                            \        ENTRY(0x39, "\u00A0")                            \        ENTRY(0x3a, "\u00e8")                            \        ENTRY(0x3b, "\u00e2")                            \        ENTRY(0x3c, "\u00ea")                            \        ENTRY(0x3d, "\u00ee")                            \        ENTRY(0x3e, "\u00f4")                            \        ENTRY(0x3f, "\u00fb")                            \    END_SET                                              \    START_SET(CCSET_EXTENDED_SPANISH_FRENCH_MISC)        \        ENTRY(0x20, "\u00c1")                            \        ENTRY(0x21, "\u00c9")                            \        ENTRY(0x22, "\u00d3")                            \        ENTRY(0x23, "\u00da")                            \        ENTRY(0x24, "\u00dc")                            \        ENTRY(0x25, "\u00fc")                            \        ENTRY(0x26, "\u00b4")                            \        ENTRY(0x27, "\u00a1")                            \        ENTRY(0x28, "*")                                 \        ENTRY(0x29, "\u2018")                            \        ENTRY(0x2a, "-")                                 \        ENTRY(0x2b, "\u00a9")                            \        ENTRY(0x2c, "\u2120")                            \        ENTRY(0x2d, "\u00b7")                            \        ENTRY(0x2e, "\u201c")                            \        ENTRY(0x2f, "\u201d")                            \        ENTRY(0x30, "\u00c0")                            \        ENTRY(0x31, "\u00c2")                            \        ENTRY(0x32, "\u00c7")                            \        ENTRY(0x33, "\u00c8")                            \        ENTRY(0x34, "\u00ca")                            \        ENTRY(0x35, "\u00cb")                            \        ENTRY(0x36, "\u00eb")                            \        ENTRY(0x37, "\u00ce")                            \        ENTRY(0x38, "\u00cf")                            \        ENTRY(0x39, "\u00ef")                            \        ENTRY(0x3a, "\u00d4")                            \        ENTRY(0x3b, "\u00d9")                            \        ENTRY(0x3c, "\u00f9")                            \        ENTRY(0x3d, "\u00db")                            \        ENTRY(0x3e, "\u00ab")                            \        ENTRY(0x3f, "\u00bb")                            \    END_SET                                              \    START_SET(CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH)   \        ENTRY(0x20, "\u00c3")                            \        ENTRY(0x21, "\u00e3")                            \        ENTRY(0x22, "\u00cd")                            \        ENTRY(0x23, "\u00cc")                            \        ENTRY(0x24, "\u00ec")                            \        ENTRY(0x25, "\u00d2")                            \        ENTRY(0x26, "\u00f2")                            \        ENTRY(0x27, "\u00d5")                            \        ENTRY(0x28, "\u00f5")                            \        ENTRY(0x29, "{")                                 \        ENTRY(0x2a, "}")                                 \        ENTRY(0x2b, "\\")                                \        ENTRY(0x2c, "^")                                 \        ENTRY(0x2d, "_")                                 \        ENTRY(0x2e, "|")                                 \        ENTRY(0x2f, "~")                                 \        ENTRY(0x30, "\u00c4")                            \        ENTRY(0x31, "\u00e4")                            \        ENTRY(0x32, "\u00d6")                            \        ENTRY(0x33, "\u00f6")                            \        ENTRY(0x34, "\u00df")                            \        ENTRY(0x35, "\u00a5")                            \        ENTRY(0x36, "\u00a4")                            \        ENTRY(0x37, "\u00a6")                            \        ENTRY(0x38, "\u00c5")                            \        ENTRY(0x39, "\u00e5")                            \        ENTRY(0x3a, "\u00d8")                            \        ENTRY(0x3b, "\u00f8")                            \        ENTRY(0x3c, "\u250c")                            \        ENTRY(0x3d, "\u2510")                            \        ENTRY(0x3e, "\u2514")                            \        ENTRY(0x3f, "\u2518")                            \    END_SET                                              \ static const char charset_overrides[4][128][sizeof("\u266a")] ={#define START_SET(IDX) \    [IDX] = {#define ENTRY(idx, string) \        [idx] = string,#define END_SET \    },    CHARSET_OVERRIDE_LIST(START_SET, ENTRY, END_SET)};#define EMPTY_START(IDX)#define EMPTY_END#define ASSERT_ENTRY(IDX, str)                                    \    static_assert(sizeof(str) <= sizeof(charset_overrides[0][0]), \                  "'" str "' string takes too much space");CHARSET_OVERRIDE_LIST(EMPTY_START, ASSERT_ENTRY, EMPTY_END) static const unsigned char bg_attribs[8] = // Color{    CCCOL_WHITE,    CCCOL_GREEN,    CCCOL_BLUE,    CCCOL_CYAN,    CCCOL_RED,    CCCOL_YELLOW,    CCCOL_MAGENTA,    CCCOL_BLACK,}; static const unsigned char pac2_attribs[32][3] = // Color, font, ident{    { 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    /* total 32 entries */}; struct Screen {    /* +1 is used to compensate null character of string */    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 bgs[SCREEN_ROWS+1][SCREEN_COLUMNS+1];    uint8_t fonts[SCREEN_ROWS+1][SCREEN_COLUMNS+1];    /*     * Bitmask of used rows; if a bit is not set, the     * corresponding row is not used.     * for setting row 1  use row | (1 << 0)     * for setting row 15 use row | (1 << 14)     */    int16_t row_used;}; typedef struct CCaptionSubContext {    AVClass *class;    int real_time;    int real_time_latency_msec;    int data_field;    struct Screen screen[2];    int active_screen;    uint8_t cursor_row;    uint8_t cursor_column;    uint8_t cursor_color;    uint8_t bg_color;    uint8_t cursor_font;    uint8_t cursor_charset;    AVBPrint buffer[2];    int buffer_index;    int buffer_changed;    int rollup;    enum cc_mode mode;    int64_t buffer_time[2];    int screen_touched;    int64_t last_real_time;    uint8_t prev_cmd[2];    int readorder;} CCaptionSubContext; static av_cold int init_decoder(AVCodecContext *avctx){    CCaptionSubContext *ctx = avctx->priv_data;     av_bprint_init(&ctx->buffer[0], 0, AV_BPRINT_SIZE_UNLIMITED);    av_bprint_init(&ctx->buffer[1], 0, AV_BPRINT_SIZE_UNLIMITED);    /* taking by default roll up to 2 */    ctx->mode = CCMODE_ROLLUP;    ctx->bg_color = CCCOL_BLACK;    ctx->rollup = 2;    ctx->cursor_row = 10;    return 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);} static av_cold int close_decoder(AVCodecContext *avctx){    CCaptionSubContext *ctx = avctx->priv_data;    av_bprint_finalize(&ctx->buffer[0], NULL);    av_bprint_finalize(&ctx->buffer[1], NULL);    return 0;} 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;    ctx->cursor_row = 10;    ctx->cursor_column = 0;    ctx->cursor_font = 0;    ctx->cursor_color = 0;    ctx->bg_color = CCCOL_BLACK;    ctx->cursor_charset = 0;    ctx->active_screen = 0;    ctx->last_real_time = 0;    ctx->screen_touched = 0;    ctx->buffer_changed = 0;    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))        ctx->readorder = 0;    av_bprint_clear(&ctx->buffer[0]);    av_bprint_clear(&ctx->buffer[1]);} /** * @param ctx closed caption context just to print log */static void write_char(CCaptionSubContext *ctx, struct Screen *screen, char ch){    uint8_t col = ctx->cursor_column;    char *row = screen->characters[ctx->cursor_row];    char *font = screen->fonts[ctx->cursor_row];    char *color = screen->colors[ctx->cursor_row];    char *bg = screen->bgs[ctx->cursor_row];    char *charset = screen->charsets[ctx->cursor_row];     if (col < SCREEN_COLUMNS) {        row[col] = ch;        font[col] = ctx->cursor_font;        color[col] = ctx->cursor_color;        bg[col] = ctx->bg_color;        charset[col] = ctx->cursor_charset;        ctx->cursor_charset = CCSET_BASIC_AMERICAN;        if (ch) ctx->cursor_column++;        return;    }    /* We have extra space at end only for null character */    else if (col == SCREEN_COLUMNS && ch == 0) {        row[col] = ch;        return;    }    else {        av_log(ctx, AV_LOG_WARNING, "Data ignored due to columns exceeding screen width\n");        return;    }} /** * 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. */static int validate_cc_data_pair(const uint8_t *cc_data_pair, uint8_t *hi){    uint8_t cc_valid = (*cc_data_pair & 4) >>2;    uint8_t cc_type = *cc_data_pair & 3;     *hi = cc_data_pair[1];     if (!cc_valid)        return AVERROR_INVALIDDATA;     // if EIA-608 data then verify parity.    if (cc_type==0 || cc_type==1) {        if (!av_parity(cc_data_pair[2])) {            return AVERROR_INVALIDDATA;        }        if (!av_parity(cc_data_pair[1])) {            *hi = 0x7F;        }    }     //Skip non-data    if ((cc_data_pair[0] == 0xFA || cc_data_pair[0] == 0xFC || cc_data_pair[0] == 0xFD)         && (cc_data_pair[1] & 0x7F) == 0 && (cc_data_pair[2] & 0x7F) == 0)        return AVERROR_PATCHWELCOME;     //skip 708 data    if (cc_type == 3 || cc_type == 2)        return AVERROR_PATCHWELCOME;     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:    case CCMODE_ROLLUP:    case CCMODE_TEXT:        // use active screen        return ctx->screen + ctx->active_screen;    }    /* It was never an option */    return NULL;} static void roll_up(CCaptionSubContext *ctx){    struct Screen *screen;    int i, keep_lines;     if (ctx->mode == CCMODE_TEXT)        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);     for (i = 0; i < SCREEN_ROWS; i++) {        if (i > ctx->cursor_row - keep_lines && i <= ctx->cursor_row)            continue;        UNSET_FLAG(screen->row_used, i);    }     for (i = 0; i < keep_lines && screen->row_used; i++) {        const int i_row = ctx->cursor_row - keep_lines + i + 1;         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->bgs[i_row], screen->bgs[i_row+1], SCREEN_COLUMNS);        memcpy(screen->fonts[i_row], screen->fonts[i_row+1], SCREEN_COLUMNS);        memcpy(screen->charsets[i_row], screen->charsets[i_row+1], SCREEN_COLUMNS);        if (CHECK_FLAG(screen->row_used, i_row + 1))            SET_FLAG(screen->row_used, i_row);    }     UNSET_FLAG(screen->row_used, ctx->cursor_row);} static int capture_screen(CCaptionSubContext *ctx){    int i, j, tab = 0;    struct Screen *screen = ctx->screen + ctx->active_screen;    enum cc_font prev_font = CCFONT_REGULAR;    enum cc_color_code prev_color = CCCOL_WHITE;    enum cc_color_code prev_bg_color = CCCOL_BLACK;    const int bidx = ctx->buffer_index;     av_bprint_clear(&ctx->buffer[bidx]);     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;        }    }     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 *font = screen->fonts[i];            const char *bg = screen->bgs[i];            const char *color = screen->colors[i];            const char *charset = screen->charsets[i];            const char *override;            int x, y, seen_char = 0;            j = 0;             /* skip leading space */            while (row[j] == ' ' && charset[j] == CCSET_BASIC_AMERICAN && j < tab)                j++;             x = ASS_DEFAULT_PLAYRESX * (0.1 + 0.0250 * j);            y = ASS_DEFAULT_PLAYRESY * (0.1 + 0.0533 * i);            av_bprintf(&ctx->buffer[bidx], "{\\an7}{\\pos(%d,%d)}", x, y);

Lines 1–500 of 970.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t