[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