[FFmpeg-devel] [PATCH] avcodec/webvttenc: add webvtt encoder

Timothy Gu timothygu99 at gmail.com
Sun May 11 23:58:02 CEST 2014


On Sun, May 11, 2014 at 2:38 PM,  <ffmpeg at tmm1.net> wrote:
> From: Aman Gupta <ffmpeg at tmm1.net>
>
> Mostly a copy of the srt encoder in libavcodec/srtenc.c.
> The alignment and move callbacks are unimplemented, but the rest works well.
> With this patch, ffmpeg can be used to convert subtitles into the .vtt format:
>
>   ffmpeg -i input.srt -c:s webvtt output.vtt
>
> Signed-off-by: Aman Gupta <ffmpeg at tmm1.net>
> ---
>  Changelog              |   1 +
>  libavcodec/Makefile    |   1 +
>  libavcodec/allcodecs.c |   2 +-
>  libavcodec/webvttenc.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 258 insertions(+), 1 deletion(-)
>  create mode 100644 libavcodec/webvttenc.c

[...]

> +static char webvtt_stack_push(WebVTTContext *s, char c)

static int

> +{
> +    if (s->stack_ptr >= WEBVTT_STACK_SIZE)
> +        return -1;

AVERROR code

> +    s->stack[s->stack_ptr++] = c;
> +    return 0;
> +}
> +

> +static int webvtt_stack_find(WebVTTContext *s, const char c)
> +{
> +    int i;
> +    for (i = s->stack_ptr-1; i >= 0; i--)
> +        if (s->stack[i] == c)
> +            break;
> +    return i;
> +}
> +
> +static void webvtt_close_tag(WebVTTContext *s, char tag)
> +{
> +    webvtt_print(s, "</%c%s>", tag, tag == 'f' ? "ont" : "");
> +}
> +
> +static void webvtt_stack_push_pop(WebVTTContext *s, const char c, int close)
> +{
> +    if (close) {
> +        int i = c ? webvtt_stack_find(s, c) : 0;
> +        if (i < 0)
> +            return;
> +        while (s->stack_ptr != i)
> +            webvtt_close_tag(s, webvtt_stack_pop(s));
> +    } else if (webvtt_stack_push(s, c) < 0)
> +        av_log(s->avctx, AV_LOG_ERROR, "tag stack overflow\n");
> +}
> +
> +static void webvtt_style_apply(WebVTTContext *s, const char *style)
> +{
> +    ASSStyle *st = ff_ass_style_get(s->ass_ctx, style);
> +    if (st) {
> +        int c = st->primary_color & 0xFFFFFF;
> +        if (st->font_name && strcmp(st->font_name, ASS_DEFAULT_FONT) ||
> +            st->font_size != ASS_DEFAULT_FONT_SIZE ||
> +            c != ASS_DEFAULT_COLOR) {
> +            webvtt_print(s, "<font");
> +            if (st->font_name && strcmp(st->font_name, ASS_DEFAULT_FONT))
> +                webvtt_print(s, " face=\"%s\"", st->font_name);
> +            if (st->font_size != ASS_DEFAULT_FONT_SIZE)
> +                webvtt_print(s, " size=\"%d\"", st->font_size);
> +            if (c != ASS_DEFAULT_COLOR)
> +                webvtt_print(s, " color=\"#%06x\"",
> +                          (c & 0xFF0000) >> 16 | c & 0xFF00 | (c & 0xFF) << 16);
> +            webvtt_print(s, ">");
> +            webvtt_stack_push(s, 'f');
> +        }
> +        if (st->bold != ASS_DEFAULT_BOLD) {
> +            webvtt_print(s, "<b>");
> +            webvtt_stack_push(s, 'b');
> +        }
> +        if (st->italic != ASS_DEFAULT_ITALIC) {
> +            webvtt_print(s, "<i>");
> +            webvtt_stack_push(s, 'i');
> +        }
> +        if (st->underline != ASS_DEFAULT_UNDERLINE) {
> +            webvtt_print(s, "<u>");
> +            webvtt_stack_push(s, 'u');
> +        }
> +    }
> +}
> +
> +static void webvtt_text_cb(void *priv, const char *text, int len)
> +{
> +    WebVTTContext *s = priv;
> +    av_bprint_append_data(&s->buffer, text, len);
> +}
> +
> +static void webvtt_new_line_cb(void *priv, int forced)
> +{
> +    webvtt_print(priv, "\r\n");
> +}
> +
> +static void webvtt_style_cb(void *priv, char style, int close)
> +{
> +    webvtt_stack_push_pop(priv, style, close);
> +    if (!close)
> +        webvtt_print(priv, "<%c>", style);
> +}
> +
> +static void webvtt_color_cb(void *priv, unsigned int color, unsigned int color_id)
> +{
> +    if (color_id > 1)
> +        return;
> +    webvtt_stack_push_pop(priv, 'f', color == 0xFFFFFFFF);
> +    if (color != 0xFFFFFFFF)
> +        webvtt_print(priv, "<font color=\"#%06x\">",
> +              (color & 0xFF0000) >> 16 | color & 0xFF00 | (color & 0xFF) << 16);
> +}
> +
> +static void webvtt_font_name_cb(void *priv, const char *name)
> +{
> +    webvtt_stack_push_pop(priv, 'f', !name);
> +    if (name)
> +        webvtt_print(priv, "<font face=\"%s\">", name);
> +}
> +
> +static void webvtt_font_size_cb(void *priv, int size)
> +{
> +    webvtt_stack_push_pop(priv, 'f', size < 0);
> +    if (size >= 0)
> +        webvtt_print(priv, "<font size=\"%d\">", size);
> +}
> +
> +static void webvtt_cancel_overrides_cb(void *priv, const char *style)
> +{
> +    webvtt_stack_push_pop(priv, 0, 1);
> +    webvtt_style_apply(priv, style);
> +}
> +
> +static void webvtt_end_cb(void *priv)
> +{
> +    webvtt_stack_push_pop(priv, 0, 1);
> +}
> +
> +static const ASSCodesCallbacks webvtt_callbacks = {
> +    .text             = webvtt_text_cb,
> +    .new_line         = webvtt_new_line_cb,
> +    .style            = webvtt_style_cb,
> +    .color            = webvtt_color_cb,
> +    .font_name        = webvtt_font_name_cb,
> +    .font_size        = webvtt_font_size_cb,
> +    .alignment        = NULL,
> +    .cancel_overrides = webvtt_cancel_overrides_cb,
> +    .move             = NULL,
> +    .end              = webvtt_end_cb,
> +};
> +
> +static int webvtt_encode_frame(AVCodecContext *avctx,
> +                               unsigned char *buf, int bufsize, const AVSubtitle *sub)
> +{
> +    WebVTTContext *s = avctx->priv_data;
> +    ASSDialog *dialog;
> +    int i, num;
> +
> +    av_bprint_clear(&s->buffer);
> +
> +    for (i=0; i<sub->num_rects; i++) {
> +
> +        if (sub->rects[i]->type != SUBTITLE_ASS) {
> +            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
> +            return AVERROR(ENOSYS);
> +        }
> +
> +        dialog = ff_ass_split_dialog(s->ass_ctx, sub->rects[i]->ass, 0, &num);
> +        for (; dialog && num--; dialog++) {
> +            webvtt_style_apply(s, dialog->style);
> +            ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
> +        }
> +    }
> +
> +    if (!av_bprint_is_complete(&s->buffer))
> +        return AVERROR(ENOMEM);
> +    if (!s->buffer.len)
> +        return 0;
> +
> +    if (s->buffer.len > bufsize) {
> +        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
> +        return -1;
> +    }
> +    memcpy(buf, s->buffer.str, s->buffer.len);
> +
> +    return s->buffer.len;
> +}
> +
> +static int webvtt_encode_close(AVCodecContext *avctx)
> +{
> +    WebVTTContext *s = avctx->priv_data;
> +    ff_ass_split_free(s->ass_ctx);
> +    av_bprint_finalize(&s->buffer, NULL);
> +    return 0;
> +}
> +
> +static av_cold int webvtt_encode_init(AVCodecContext *avctx)
> +{
> +    WebVTTContext *s = avctx->priv_data;
> +    s->avctx = avctx;
> +    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
> +    av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
> +    return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
> +}
> +
> +AVCodec ff_webvtt_encoder = {
> +    .name           = "webvtt",
> +    .long_name      = NULL_IF_CONFIG_SMALL("WebVTT subtitle"),
> +    .type           = AVMEDIA_TYPE_SUBTITLE,
> +    .id             = AV_CODEC_ID_WEBVTT,
> +    .priv_data_size = sizeof(WebVTTContext),
> +    .init           = webvtt_encode_init,
> +    .encode_sub     = webvtt_encode_frame,
> +    .close          = webvtt_encode_close,
> +};
> --
> 1.9.1
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel


More information about the ffmpeg-devel mailing list