1/* 2 * WebVTT subtitle encoder 3 * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org> 4 * Copyright (c) 2014 Aman Gupta <ffmpeg@tmm1.net> 5 * 6 * This file is part of FFmpeg. 7 * 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. 12 * 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. 17 * 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 21 */ 22 23#include <stdarg.h> 24#include "avcodec.h" 25#include "libavutil/avstring.h" 26#include "libavutil/bprint.h" 27#include "ass_split.h" 28#include "ass.h" 29#include "codec_internal.h" 30 31#define WEBVTT_STACK_SIZE 64 32typedef struct { 33 AVCodecContext *avctx; 34 ASSSplitContext *ass_ctx; 35 AVBPrint buffer; 36 unsigned timestamp_end; 37 int count; 38 char stack[WEBVTT_STACK_SIZE]; 39 int stack_ptr; 40} WebVTTContext; 41 42#ifdef __GNUC__ 43__attribute__ ((__format__ (__printf__, 2, 3))) 44#endif 45static void webvtt_print(WebVTTContext *s, const char *str, ...) 46{ 47 va_list vargs; 48 va_start(vargs, str); 49 av_vbprintf(&s->buffer, str, vargs); 50 va_end(vargs); 51} 52 53static int webvtt_stack_push(WebVTTContext *s, const char c) 54{ 55 if (s->stack_ptr >= WEBVTT_STACK_SIZE) 56 return -1; 57 s->stack[s->stack_ptr++] = c; 58 return 0; 59} 60 61static char webvtt_stack_pop(WebVTTContext *s) 62{ 63 if (s->stack_ptr <= 0) 64 return 0; 65 return s->stack[--s->stack_ptr]; 66} 67 68static int webvtt_stack_find(WebVTTContext *s, const char c) 69{ 70 int i; 71 for (i = s->stack_ptr-1; i >= 0; i--) 72 if (s->stack[i] == c) 73 break; 74 return i; 75} 76 77static void webvtt_close_tag(WebVTTContext *s, char tag) 78{ 79 webvtt_print(s, "</%c>", tag); 80} 81 82static void webvtt_stack_push_pop(WebVTTContext *s, const char c, int close) 83{ 84 if (close) { 85 int i = c ? webvtt_stack_find(s, c) : 0; 86 if (i < 0) 87 return; 88 while (s->stack_ptr != i) 89 webvtt_close_tag(s, webvtt_stack_pop(s)); 90 } else if (webvtt_stack_push(s, c) < 0) 91 av_log(s->avctx, AV_LOG_ERROR, "tag stack overflow\n"); 92} 93 94static void webvtt_style_apply(WebVTTContext *s, const char *style) 95{ 96 ASSStyle *st = ff_ass_style_get(s->ass_ctx, style); 97 if (st) { 98 if (st->bold != ASS_DEFAULT_BOLD) { 99 webvtt_print(s, "<b>"); 100 webvtt_stack_push(s, 'b'); 101 } 102 if (st->italic != ASS_DEFAULT_ITALIC) { 103 webvtt_print(s, "<i>"); 104 webvtt_stack_push(s, 'i'); 105 } 106 if (st->underline != ASS_DEFAULT_UNDERLINE) { 107 webvtt_print(s, "<u>"); 108 webvtt_stack_push(s, 'u'); 109 } 110 } 111} 112 113static void webvtt_text_cb(void *priv, const char *text, int len) 114{ 115 WebVTTContext *s = priv; 116 av_bprint_append_data(&s->buffer, text, len); 117} 118 119static void webvtt_new_line_cb(void *priv, int forced) 120{ 121 webvtt_print(priv, "\n"); 122} 123 124static void webvtt_style_cb(void *priv, char style, int close) 125{ 126 if (style == 's') // strikethrough unsupported 127 return; 128 129 webvtt_stack_push_pop(priv, style, close); 130 if (!close) 131 webvtt_print(priv, "<%c>", style); 132} 133 134static void webvtt_cancel_overrides_cb(void *priv, const char *style) 135{ 136 webvtt_stack_push_pop(priv, 0, 1); 137 webvtt_style_apply(priv, style); 138} 139 140static void webvtt_end_cb(void *priv) 141{ 142 webvtt_stack_push_pop(priv, 0, 1); 143} 144 145static const ASSCodesCallbacks webvtt_callbacks = { 146 .text = webvtt_text_cb, 147 .new_line = webvtt_new_line_cb, 148 .style = webvtt_style_cb, 149 .color = NULL, 150 .font_name = NULL, 151 .font_size = NULL, 152 .alignment = NULL, 153 .cancel_overrides = webvtt_cancel_overrides_cb, 154 .move = NULL, 155 .end = webvtt_end_cb, 156}; 157 158static int webvtt_encode_frame(AVCodecContext *avctx, 159 unsigned char *buf, int bufsize, const AVSubtitle *sub) 160{ 161 WebVTTContext *s = avctx->priv_data; 162 ASSDialog *dialog; 163 int i; 164 165 av_bprint_clear(&s->buffer); 166 167 for (i=0; i<sub->num_rects; i++) { 168 const char *ass = sub->rects[i]->ass; 169 170 if (sub->rects[i]->type != SUBTITLE_ASS) { 171 av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); 172 return AVERROR(EINVAL); 173 } 174 175 dialog = ff_ass_split_dialog(s->ass_ctx, ass); 176 if (!dialog) 177 return AVERROR(ENOMEM); 178 webvtt_style_apply(s, dialog->style); 179 ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text); 180 ff_ass_free_dialog(&dialog); 181 } 182 183 if (!av_bprint_is_complete(&s->buffer)) 184 return AVERROR(ENOMEM); 185 if (!s->buffer.len) 186 return 0; 187 188 if (s->buffer.len > bufsize) { 189 av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); 190 return AVERROR_BUFFER_TOO_SMALL; 191 } 192 memcpy(buf, s->buffer.str, s->buffer.len); 193 194 return s->buffer.len; 195} 196 197static int webvtt_encode_close(AVCodecContext *avctx) 198{ 199 WebVTTContext *s = avctx->priv_data; 200 ff_ass_split_free(s->ass_ctx); 201 av_bprint_finalize(&s->buffer, NULL); 202 return 0; 203} 204 205static av_cold int webvtt_encode_init(AVCodecContext *avctx) 206{ 207 WebVTTContext *s = avctx->priv_data; 208 s->avctx = avctx; 209 s->ass_ctx = ff_ass_split(avctx->subtitle_header); 210 av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); 211 return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; 212} 213 214const FFCodec ff_webvtt_encoder = { 215 .p.name = "webvtt", 216 .p.long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"), 217 .p.type = AVMEDIA_TYPE_SUBTITLE, 218 .p.id = AV_CODEC_ID_WEBVTT, 219 .priv_data_size = sizeof(WebVTTContext), 220 .init = webvtt_encode_init, 221 FF_CODEC_ENCODE_SUB_CB(webvtt_encode_frame), 222 .close = webvtt_encode_close, 223 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 224}; 225