2 * WebVTT subtitle encoder
3 * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org>
4 * Copyright (c) 2014 Aman Gupta <ffmpeg@tmm1.net>
6 * This file is part of FFmpeg.
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "libavutil/avstring.h"
26 #include "libavutil/bprint.h"
27 #include "ass_split.h"
30 #define WEBVTT_STACK_SIZE 64
32 AVCodecContext
*avctx
;
33 ASSSplitContext
*ass_ctx
;
35 unsigned timestamp_end
;
37 char stack
[WEBVTT_STACK_SIZE
];
42 __attribute__ ((__format__ (__printf__
, 2, 3)))
44 static void webvtt_print(WebVTTContext
*s
, const char *str
, ...)
48 av_vbprintf(&s
->buffer
, str
, vargs
);
52 static int webvtt_stack_push(WebVTTContext
*s
, const char c
)
54 if (s
->stack_ptr
>= WEBVTT_STACK_SIZE
)
56 s
->stack
[s
->stack_ptr
++] = c
;
60 static char webvtt_stack_pop(WebVTTContext
*s
)
62 if (s
->stack_ptr
<= 0)
64 return s
->stack
[--s
->stack_ptr
];
67 static int webvtt_stack_find(WebVTTContext
*s
, const char c
)
70 for (i
= s
->stack_ptr
-1; i
>= 0; i
--)
76 static void webvtt_close_tag(WebVTTContext
*s
, char tag
)
78 webvtt_print(s
, "</%c>", tag
);
81 static void webvtt_stack_push_pop(WebVTTContext
*s
, const char c
, int close
)
84 int i
= c
? webvtt_stack_find(s
, c
) : 0;
87 while (s
->stack_ptr
!= i
)
88 webvtt_close_tag(s
, webvtt_stack_pop(s
));
89 } else if (webvtt_stack_push(s
, c
) < 0)
90 av_log(s
->avctx
, AV_LOG_ERROR
, "tag stack overflow\n");
93 static void webvtt_style_apply(WebVTTContext
*s
, const char *style
)
95 ASSStyle
*st
= ff_ass_style_get(s
->ass_ctx
, style
);
97 if (st
->bold
!= ASS_DEFAULT_BOLD
) {
98 webvtt_print(s
, "<b>");
99 webvtt_stack_push(s
, 'b');
101 if (st
->italic
!= ASS_DEFAULT_ITALIC
) {
102 webvtt_print(s
, "<i>");
103 webvtt_stack_push(s
, 'i');
105 if (st
->underline
!= ASS_DEFAULT_UNDERLINE
) {
106 webvtt_print(s
, "<u>");
107 webvtt_stack_push(s
, 'u');
112 static void webvtt_text_cb(void *priv
, const char *text
, int len
)
114 WebVTTContext
*s
= priv
;
115 av_bprint_append_data(&s
->buffer
, text
, len
);
118 static void webvtt_new_line_cb(void *priv
, int forced
)
120 webvtt_print(priv
, "\n");
123 static void webvtt_style_cb(void *priv
, char style
, int close
)
125 if (style
== 's') // strikethrough unsupported
128 webvtt_stack_push_pop(priv
, style
, close
);
130 webvtt_print(priv
, "<%c>", style
);
133 static void webvtt_cancel_overrides_cb(void *priv
, const char *style
)
135 webvtt_stack_push_pop(priv
, 0, 1);
136 webvtt_style_apply(priv
, style
);
139 static void webvtt_end_cb(void *priv
)
141 webvtt_stack_push_pop(priv
, 0, 1);
144 static const ASSCodesCallbacks webvtt_callbacks
= {
145 .text
= webvtt_text_cb
,
146 .new_line
= webvtt_new_line_cb
,
147 .style
= webvtt_style_cb
,
152 .cancel_overrides
= webvtt_cancel_overrides_cb
,
154 .end
= webvtt_end_cb
,
157 static int webvtt_encode_frame(AVCodecContext
*avctx
,
158 unsigned char *buf
, int bufsize
, const AVSubtitle
*sub
)
160 WebVTTContext
*s
= avctx
->priv_data
;
164 av_bprint_clear(&s
->buffer
);
166 for (i
=0; i
<sub
->num_rects
; i
++) {
167 if (sub
->rects
[i
]->type
!= SUBTITLE_ASS
) {
168 av_log(avctx
, AV_LOG_ERROR
, "Only SUBTITLE_ASS type supported.\n");
169 return AVERROR(ENOSYS
);
172 dialog
= ff_ass_split_dialog(s
->ass_ctx
, sub
->rects
[i
]->ass
, 0, &num
);
173 for (; dialog
&& num
--; dialog
++) {
174 webvtt_style_apply(s
, dialog
->style
);
175 ff_ass_split_override_codes(&webvtt_callbacks
, s
, dialog
->text
);
179 if (!av_bprint_is_complete(&s
->buffer
))
180 return AVERROR(ENOMEM
);
184 if (s
->buffer
.len
> bufsize
) {
185 av_log(avctx
, AV_LOG_ERROR
, "Buffer too small for ASS event.\n");
188 memcpy(buf
, s
->buffer
.str
, s
->buffer
.len
);
190 return s
->buffer
.len
;
193 static int webvtt_encode_close(AVCodecContext
*avctx
)
195 WebVTTContext
*s
= avctx
->priv_data
;
196 ff_ass_split_free(s
->ass_ctx
);
197 av_bprint_finalize(&s
->buffer
, NULL
);
201 static av_cold
int webvtt_encode_init(AVCodecContext
*avctx
)
203 WebVTTContext
*s
= avctx
->priv_data
;
205 s
->ass_ctx
= ff_ass_split(avctx
->subtitle_header
);
206 av_bprint_init(&s
->buffer
, 0, AV_BPRINT_SIZE_UNLIMITED
);
207 return s
->ass_ctx
? 0 : AVERROR_INVALIDDATA
;
210 AVCodec ff_webvtt_encoder
= {
212 .long_name
= NULL_IF_CONFIG_SMALL("WebVTT subtitle"),
213 .type
= AVMEDIA_TYPE_SUBTITLE
,
214 .id
= AV_CODEC_ID_WEBVTT
,
215 .priv_data_size
= sizeof(WebVTTContext
),
216 .init
= webvtt_encode_init
,
217 .encode_sub
= webvtt_encode_frame
,
218 .close
= webvtt_encode_close
,