[FFmpeg-devel] [PATCH] 3GPP TS 26.245 Timed Text decoder: decode rich formatting in sample modifier boxes

Philip Langdale philipl at overt.org
Wed Mar 18 06:03:47 CET 2015


On Tue, 17 Mar 2015 21:09:44 -0700
Wesley Castro <root670 at gmail.com> wrote:

> Bold, italic, and underlined type will be converted to their
> equivilent ASS styles when encountered in a text style sample
> modifier box.
> 
> Signed-off-by: Wesley Castro <root670 at gmail.com>
> ---

Hi Wesley,

Thanks for sending this first change. We're off to a good start.

>  libavcodec/movtextdec.c | 72
> ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69
> insertions(+), 3 deletions(-)
> 
> diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c
> index 1c7ffea..4dba1e4 100644
> --- a/libavcodec/movtextdec.c
> +++ b/libavcodec/movtextdec.c
> @@ -26,9 +26,25 @@
>  #include "libavutil/bprint.h"
>  #include "libavutil/intreadwrite.h"
>  
> -static int text_to_ass(AVBPrint *buf, const char *text, const char
> *text_end) +#define STYLE_FLAG_BOLD         1
> +#define STYLE_FLAG_ITALIC       2
> +#define STYLE_FLAG_UNDERLINE    4
> +
> +static int text_to_ass(AVBPrint *buf, const char *text, const char
> *text_end,
> +                       const char *style_start, const char
> *style_end,
> +                       const int style_flags)
>  {
>      while (text < text_end) {
> +        if (style_flags && text == style_start)
> +        {
> +            if (style_flags & STYLE_FLAG_BOLD)
> +                av_bprintf(buf, "{\\b1}");
> +            if (style_flags & STYLE_FLAG_ITALIC)
> +                av_bprintf(buf, "{\\i1}");
> +            if (style_flags & STYLE_FLAG_UNDERLINE)
> +                av_bprintf(buf, "{\\u1}");
> +        }
> +
>          switch (*text) {
>          case '\r':
>              break;
> @@ -36,9 +52,22 @@ static int text_to_ass(AVBPrint *buf, const char
> *text, const char *text_end) av_bprintf(buf, "\\N");
>              break;
>          default:
> +
>              av_bprint_chars(buf, *text, 1);
> +
>              break;
>          }
> +
> +        if (style_flags && text == style_end)
> +        {
> +            if (style_flags & STYLE_FLAG_BOLD)
> +                av_bprintf(buf, "{\\b1}");
> +            if (style_flags & STYLE_FLAG_ITALIC)
> +                av_bprintf(buf, "{\\i1}");
> +            if (style_flags & STYLE_FLAG_UNDERLINE)
> +                av_bprintf(buf, "{\\u1}");
> +        }
> +
>          text++;
>      }
>  
> @@ -63,6 +92,9 @@ static int mov_text_decode_frame(AVCodecContext
> *avctx, AVBPrint buf;
>      const char *ptr = avpkt->data;
>      const char *end;
> +    int text_length, tsmb_type, style_entries, style_flags;
> +    const char *style_start, *style_end;
> +    const uint8_t *tsmb;
>  
>      if (!ptr || avpkt->size < 2)
>          return AVERROR_INVALIDDATA;
> @@ -82,7 +114,9 @@ static int mov_text_decode_frame(AVCodecContext
> *avctx,
>       * In complex cases, there are style descriptors appended to the
> string
>       * so we can't just assume the packet size is the string size.
>       */
> -    end = ptr + FFMIN(2 + AV_RB16(ptr), avpkt->size);
> +    text_length = AV_RB16(ptr);
> +
> +    end = ptr + FFMIN(2 + text_length, avpkt->size);
>      ptr += 2;
>  
>      ts_start = av_rescale_q(avpkt->pts,
> @@ -94,7 +128,39 @@ static int mov_text_decode_frame(AVCodecContext
> *avctx, 
>      // Note that the spec recommends lines be no longer than 2048
> characters. av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
> -    text_to_ass(&buf, ptr, end);
> +
> +    if (text_length + 2 != avpkt->size)
> +    {
> +        tsmb = ptr + text_length;
> +
> +        //tsmb_size = AV_RB32(tsmb);
> +        tsmb += 4;
> +        tsmb_type = AV_RB32(tsmb);
> +        tsmb += 4;
> +
> +        if (tsmb_type == MKTAG('s','t','y','l'))

This logic doesn't work if multiple TextSampleModifierBoxes are
present. Right now it checks if there is at least one and only looks at
the first one. It's fine, for now, to only handle 'styl' boxes but you
need to be able to iterate them correctly - as there is no guarantee
that the first box will be the 'style' box.

> +        {
> +            style_entries = AV_RB16(tsmb);
> +            tsmb += 2;
> +
> +            for(int i = 0; i < style_entries;i++)
> +            {
> +                style_start = ptr + AV_RB16(tsmb);
> +                tsmb += 2;
> +                style_end = ptr + AV_RB16(tsmb);
> +                tsmb += 2;
> +                // fontID = AV_RB16(tsmb);
> +                tsmb += 2;
> +                style_flags = AV_RB8(tsmb++);
> +
> +
> +                text_to_ass(&buf, ptr, end, style_start, style_end,
> style_flags);

The logic you have here is only going to work if there is a single
style record. if there are multiple records, it will insert the same
text each time with a different style applied to the relevant subset of
the text.

You will need to break up the text into sections which a given style
applies to (and remember that text in the sample which isn't covered by
any of the style records will fall back to the default style).

> +            }
> +        }
> +    }
> +    else
> +        text_to_ass(&buf, ptr, end, NULL, NULL, 0);
> +
>      ret = ff_ass_add_rect_bprint(sub, &buf, ts_start,
> ts_end-ts_start); av_bprint_finalize(&buf, NULL);
>      if (ret < 0)

Thanks!


--phil


More information about the ffmpeg-devel mailing list