[FFmpeg-devel] [PATCH] libavcodec/ccaption_dec.c: Fixed indentation overwriting text in the cea608 caption decoder.
Aman Karmani
ffmpeg at tmm1.net
Thu Apr 8 23:24:41 EEST 2021
On Wed, Jan 27, 2021 at 3:40 PM <levi at snapstream.com> wrote:
> From: Levi Dooley <i.am.stickfigure at gmail.com>
>
> There was an assumption in the existing code that indentation would not
> occur more than once on the same row.
> This was a bad assumption. There are examples of 608 streams which call
> handle_pac multiple times on the same row with different indentation.
> As the code was before this change, the new indentation would overwrite
> existing text with spaces.
> These changes make indentation skip over columns instead. Text gets
> cleared with spaces on handle_edm.
> Instead of relying on the null character, trailing spaces are trimmed off
> the end of a row.
> This is necessary so that a null character doesn't end up between two
> words.
>
> Signed-off-by: Levi Dooley <i.am.stickfigure at gmail.com>
>
Tried this out and it looks good to me. I can confirm it fixes subtle
issues where content was previously being overwritten.
Aman
> ---
> libavcodec/ccaption_dec.c | 56 ++++++++++++++++++++++++++++++++-------
> tests/ref/fate/sub-scc | 2 +-
> 2 files changed, 47 insertions(+), 11 deletions(-)
>
> diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
> index a208e19b95..525e010897 100644
> --- a/libavcodec/ccaption_dec.c
> +++ b/libavcodec/ccaption_dec.c
> @@ -352,6 +352,17 @@ static void write_char(CCaptionSubContext *ctx,
> struct Screen *screen, char ch)
> }
> }
>
> +/**
> + * Increment the cursor_column by 1, and ensure that there are no null
> characters left behind in the row.
> + */
> +static void skip_char(CCaptionSubContext *ctx, struct Screen *screen)
> +{
> + if (!screen->characters[ctx->cursor_row][ctx->cursor_column])
> + write_char(ctx, screen, ' ');
> + else
> + ctx->cursor_column++;
> +}
> +
> /**
> * 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
> @@ -459,6 +470,7 @@ static int capture_screen(CCaptionSubContext *ctx)
> 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++;
> @@ -476,13 +488,19 @@ static int capture_screen(CCaptionSubContext *ctx)
> const char *color = screen->colors[i];
> const char *charset = screen->charsets[i];
> const char *override;
> - int x, y, seen_char = 0;
> + int x, y, row_end, seen_char = 0;
> j = 0;
>
> /* skip leading space */
> while (row[j] == ' ' && charset[j] == CCSET_BASIC_AMERICAN &&
> j < tab)
> j++;
>
> + /* skip trailing space */
> + row_end = SCREEN_COLUMNS-1;
> + while (row_end >= 0 && row[row_end] == ' ' &&
> charset[row_end] == CCSET_BASIC_AMERICAN) {
> + row_end--;
> + }
> +
> 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);
> @@ -490,7 +508,7 @@ static int capture_screen(CCaptionSubContext *ctx)
> for (; j < SCREEN_COLUMNS; j++) {
> const char *e_tag = "", *s_tag = "", *c_tag = "", *b_tag
> = "";
>
> - if (row[j] == 0)
> + if (j > row_end || row[j] == 0)
> break;
>
> if (prev_font != font[j]) {
> @@ -624,7 +642,8 @@ static void handle_textattr(CCaptionSubContext *ctx,
> uint8_t hi, uint8_t lo)
> ctx->cursor_font = pac2_attribs[i][1];
>
> SET_FLAG(screen->row_used, ctx->cursor_row);
> - write_char(ctx, screen, ' ');
> +
> + skip_char(ctx, screen);
> }
>
> static void handle_pac(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo)
> @@ -644,13 +663,13 @@ static void handle_pac(CCaptionSubContext *ctx,
> uint8_t hi, uint8_t lo)
> lo &= 0x1f;
>
> ctx->cursor_row = row_map[index] - 1;
> - ctx->cursor_color = pac2_attribs[lo][0];
> + ctx->cursor_color = pac2_attribs[lo][0];
> ctx->cursor_font = pac2_attribs[lo][1];
> ctx->cursor_charset = CCSET_BASIC_AMERICAN;
> ctx->cursor_column = 0;
> indent = pac2_attribs[lo][2];
> for (i = 0; i < indent; i++) {
> - write_char(ctx, screen, ' ');
> + skip_char(ctx, screen);
> }
> }
>
> @@ -667,6 +686,14 @@ static int handle_edm(CCaptionSubContext *ctx)
> screen->row_used = 0;
> ctx->bg_color = CCCOL_BLACK;
>
> + for (int i = 0; i < SCREEN_ROWS+1; ++i) {
> + memset(screen->characters[i], ' ',
> SCREEN_COLUMNS);
> + memset(screen->colors[i], CCCOL_WHITE,
> SCREEN_COLUMNS);
> + memset(screen->bgs[i], CCCOL_BLACK,
> SCREEN_COLUMNS);
> + memset(screen->charsets[i], CCSET_BASIC_AMERICAN,
> SCREEN_COLUMNS);
> + memset(screen->fonts[i], CCFONT_REGULAR,
> SCREEN_COLUMNS);
> + }
> +
> // In realtime mode, emit an empty caption so the last one doesn't
> // stay on the screen.
> if (ctx->real_time)
> @@ -687,6 +714,7 @@ static int handle_eoc(CCaptionSubContext *ctx)
> ret = handle_edm(ctx);
>
> ctx->cursor_column = 0;
> + ctx->cursor_row = 0;
>
> // In realtime mode, we display the buffered contents (after
> // flipping the buffer to active above) as soon as EOC arrives.
> @@ -731,7 +759,6 @@ static void handle_char(CCaptionSubContext *ctx, char
> hi, char lo)
> if (lo) {
> write_char(ctx, screen, lo);
> }
> - write_char(ctx, screen, 0);
>
> if (ctx->mode != CCMODE_POPON)
> ctx->screen_touched = 1;
> @@ -742,6 +769,18 @@ static void handle_char(CCaptionSubContext *ctx, char
> hi, char lo)
> ff_dlog(ctx, "(%c)\n", hi);
> }
>
> +static void handle_spacing(CCaptionSubContext *ctx, char lo)
> +{
> + int i;
> + struct Screen *screen = get_writing_screen(ctx);
> +
> + SET_FLAG(screen->row_used, ctx->cursor_row);
> +
> + for (i = 0; i < lo - 0x20; i++) {
> + skip_char(ctx, screen);
> + }
> +}
> +
> static int process_cc608(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo)
> {
> int ret = 0;
> @@ -823,11 +862,8 @@ static int process_cc608(CCaptionSubContext *ctx,
> uint8_t hi, uint8_t lo)
> handle_char(ctx, hi, lo);
> ctx->prev_cmd[0] = ctx->prev_cmd[1] = 0;
> } else if (hi == 0x17 && lo >= 0x21 && lo <= 0x23) {
> - int i;
> /* Tab offsets (spacing) */
> - for (i = 0; i < lo - 0x20; i++) {
> - handle_char(ctx, ' ', 0);
> - }
> + handle_spacing(ctx, lo);
> } else {
> /* Ignoring all other non data code */
> ff_dlog(ctx, "Unknown command 0x%hhx 0x%hhx\n", hi, lo);
> diff --git a/tests/ref/fate/sub-scc b/tests/ref/fate/sub-scc
> index 62cbf6fa4a..e4c1a1d004 100644
> --- a/tests/ref/fate/sub-scc
> +++ b/tests/ref/fate/sub-scc
> @@ -16,7 +16,7 @@ Dialogue:
> 0,0:00:00.69,0:00:03.29,Default,,0,0,0,,{\an7}{\pos(115,228)}[ Crowd ]
> Dialogue: 0,0:00:03.30,0:00:07.07,Default,,0,0,0,,{\an7}{\pos(38,197)}HOW
> DO YOU KNOW\N{\an7}{\pos(38,213)}SHE IS A WITCH ?\N{\an7}{\pos(153,243)}SHE
> LOOKS LIKE ONE !
> Dialogue: 0,0:00:07.07,0:00:09.27,Default,,0,0,0,,{\an7}{\pos(192,228)}[
> Shouting\N{\an7}{\pos(192,243)}\h\hAffirmations ]
> Dialogue:
> 0,0:00:09.26,0:00:11.06,Default,,0,0,0,,{\an7}{\pos(38,243)}BRING HER
> FORWARD.
> -Dialogue:
> 0,0:00:11.07,0:00:14.27,Default,,0,0,0,,{\an7}{\pos(115,228)}IâM NOT A
> WITCH.\N{\an7}{\pos(115,243)}\hIâM{\i1} NOT{\i0} A WITCH.
> +Dialogue:
> 0,0:00:11.07,0:00:14.27,Default,,0,0,0,,{\an7}{\pos(115,228)}IâM NOT A
> WITCH.\N{\an7}{\pos(115,243)}\hIâM {\i1}NOT{\i0} A WITCH.
> Dialogue: 0,0:00:14.26,0:00:16.03,Default,,0,0,0,,{\an7}{\pos(38,228)}BUT
> YOU ARE DRESSED\N{\an7}{\pos(38,243)}AS ONE.
> Dialogue:
> 0,0:00:16.03,0:00:19.03,Default,,0,0,0,,{\an7}{\pos(76,197)}THEY DRESSED ME
> UP\N{\an7}{\pos(76,213)}LIKE THIS.\N{\an7}{\pos(76,243)}\h\h\h\h\h\h\h\hNO
> ! WE DIDNâT !
> Dialogue:
> 0,0:00:19.03,0:00:22.95,Default,,0,0,0,,{\an7}{\pos(115,228)}AND THIS ISNâT
> MY NOSE.\N{\an7}{\pos(115,243)}ITâS A FALSE ONE.
> --
> 2.25.1
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
More information about the ffmpeg-devel
mailing list