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