1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * TTML subtitle encoder 3cabdff1aSopenharmony_ci * Copyright (c) 2020 24i 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * This file is part of FFmpeg. 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 11cabdff1aSopenharmony_ci * 12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15cabdff1aSopenharmony_ci * Lesser General Public License for more details. 16cabdff1aSopenharmony_ci * 17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20cabdff1aSopenharmony_ci */ 21cabdff1aSopenharmony_ci 22cabdff1aSopenharmony_ci/** 23cabdff1aSopenharmony_ci * @file 24cabdff1aSopenharmony_ci * TTML subtitle encoder 25cabdff1aSopenharmony_ci * @see https://www.w3.org/TR/ttml1/ 26cabdff1aSopenharmony_ci * @see https://www.w3.org/TR/ttml2/ 27cabdff1aSopenharmony_ci * @see https://www.w3.org/TR/ttml-imsc/rec 28cabdff1aSopenharmony_ci */ 29cabdff1aSopenharmony_ci 30cabdff1aSopenharmony_ci#include "avcodec.h" 31cabdff1aSopenharmony_ci#include "codec_internal.h" 32cabdff1aSopenharmony_ci#include "libavutil/avstring.h" 33cabdff1aSopenharmony_ci#include "libavutil/bprint.h" 34cabdff1aSopenharmony_ci#include "libavutil/internal.h" 35cabdff1aSopenharmony_ci#include "ass_split.h" 36cabdff1aSopenharmony_ci#include "ass.h" 37cabdff1aSopenharmony_ci#include "ttmlenc.h" 38cabdff1aSopenharmony_ci 39cabdff1aSopenharmony_citypedef struct { 40cabdff1aSopenharmony_ci AVCodecContext *avctx; 41cabdff1aSopenharmony_ci ASSSplitContext *ass_ctx; 42cabdff1aSopenharmony_ci AVBPrint buffer; 43cabdff1aSopenharmony_ci} TTMLContext; 44cabdff1aSopenharmony_ci 45cabdff1aSopenharmony_cistatic void ttml_text_cb(void *priv, const char *text, int len) 46cabdff1aSopenharmony_ci{ 47cabdff1aSopenharmony_ci TTMLContext *s = priv; 48cabdff1aSopenharmony_ci AVBPrint cur_line = { 0 }; 49cabdff1aSopenharmony_ci AVBPrint *buffer = &s->buffer; 50cabdff1aSopenharmony_ci 51cabdff1aSopenharmony_ci av_bprint_init(&cur_line, len, AV_BPRINT_SIZE_UNLIMITED); 52cabdff1aSopenharmony_ci 53cabdff1aSopenharmony_ci av_bprint_append_data(&cur_line, text, len); 54cabdff1aSopenharmony_ci if (!av_bprint_is_complete(&cur_line)) { 55cabdff1aSopenharmony_ci av_log(s->avctx, AV_LOG_ERROR, 56cabdff1aSopenharmony_ci "Failed to move the current subtitle dialog to AVBPrint!\n"); 57cabdff1aSopenharmony_ci av_bprint_finalize(&cur_line, NULL); 58cabdff1aSopenharmony_ci return; 59cabdff1aSopenharmony_ci } 60cabdff1aSopenharmony_ci 61cabdff1aSopenharmony_ci 62cabdff1aSopenharmony_ci av_bprint_escape(buffer, cur_line.str, NULL, AV_ESCAPE_MODE_XML, 63cabdff1aSopenharmony_ci 0); 64cabdff1aSopenharmony_ci 65cabdff1aSopenharmony_ci av_bprint_finalize(&cur_line, NULL); 66cabdff1aSopenharmony_ci} 67cabdff1aSopenharmony_ci 68cabdff1aSopenharmony_cistatic void ttml_new_line_cb(void *priv, int forced) 69cabdff1aSopenharmony_ci{ 70cabdff1aSopenharmony_ci TTMLContext *s = priv; 71cabdff1aSopenharmony_ci 72cabdff1aSopenharmony_ci av_bprintf(&s->buffer, "<br/>"); 73cabdff1aSopenharmony_ci} 74cabdff1aSopenharmony_ci 75cabdff1aSopenharmony_cistatic const ASSCodesCallbacks ttml_callbacks = { 76cabdff1aSopenharmony_ci .text = ttml_text_cb, 77cabdff1aSopenharmony_ci .new_line = ttml_new_line_cb, 78cabdff1aSopenharmony_ci}; 79cabdff1aSopenharmony_ci 80cabdff1aSopenharmony_cistatic int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, 81cabdff1aSopenharmony_ci int bufsize, const AVSubtitle *sub) 82cabdff1aSopenharmony_ci{ 83cabdff1aSopenharmony_ci TTMLContext *s = avctx->priv_data; 84cabdff1aSopenharmony_ci ASSDialog *dialog; 85cabdff1aSopenharmony_ci int i; 86cabdff1aSopenharmony_ci 87cabdff1aSopenharmony_ci av_bprint_clear(&s->buffer); 88cabdff1aSopenharmony_ci 89cabdff1aSopenharmony_ci for (i=0; i<sub->num_rects; i++) { 90cabdff1aSopenharmony_ci const char *ass = sub->rects[i]->ass; 91cabdff1aSopenharmony_ci int ret; 92cabdff1aSopenharmony_ci 93cabdff1aSopenharmony_ci if (sub->rects[i]->type != SUBTITLE_ASS) { 94cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); 95cabdff1aSopenharmony_ci return AVERROR(EINVAL); 96cabdff1aSopenharmony_ci } 97cabdff1aSopenharmony_ci 98cabdff1aSopenharmony_ci dialog = ff_ass_split_dialog(s->ass_ctx, ass); 99cabdff1aSopenharmony_ci if (!dialog) 100cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 101cabdff1aSopenharmony_ci 102cabdff1aSopenharmony_ci if (dialog->style) { 103cabdff1aSopenharmony_ci av_bprintf(&s->buffer, "<span region=\""); 104cabdff1aSopenharmony_ci av_bprint_escape(&s->buffer, dialog->style, NULL, 105cabdff1aSopenharmony_ci AV_ESCAPE_MODE_XML, 106cabdff1aSopenharmony_ci AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); 107cabdff1aSopenharmony_ci av_bprintf(&s->buffer, "\">"); 108cabdff1aSopenharmony_ci } 109cabdff1aSopenharmony_ci 110cabdff1aSopenharmony_ci ret = ff_ass_split_override_codes(&ttml_callbacks, s, dialog->text); 111cabdff1aSopenharmony_ci if (ret < 0) { 112cabdff1aSopenharmony_ci int log_level = (ret != AVERROR_INVALIDDATA || 113cabdff1aSopenharmony_ci avctx->err_recognition & AV_EF_EXPLODE) ? 114cabdff1aSopenharmony_ci AV_LOG_ERROR : AV_LOG_WARNING; 115cabdff1aSopenharmony_ci av_log(avctx, log_level, 116cabdff1aSopenharmony_ci "Splitting received ASS dialog text %s failed: %s\n", 117cabdff1aSopenharmony_ci dialog->text, 118cabdff1aSopenharmony_ci av_err2str(ret)); 119cabdff1aSopenharmony_ci 120cabdff1aSopenharmony_ci if (log_level == AV_LOG_ERROR) { 121cabdff1aSopenharmony_ci ff_ass_free_dialog(&dialog); 122cabdff1aSopenharmony_ci return ret; 123cabdff1aSopenharmony_ci } 124cabdff1aSopenharmony_ci } 125cabdff1aSopenharmony_ci 126cabdff1aSopenharmony_ci if (dialog->style) 127cabdff1aSopenharmony_ci av_bprintf(&s->buffer, "</span>"); 128cabdff1aSopenharmony_ci 129cabdff1aSopenharmony_ci ff_ass_free_dialog(&dialog); 130cabdff1aSopenharmony_ci } 131cabdff1aSopenharmony_ci 132cabdff1aSopenharmony_ci if (!av_bprint_is_complete(&s->buffer)) 133cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 134cabdff1aSopenharmony_ci if (!s->buffer.len) 135cabdff1aSopenharmony_ci return 0; 136cabdff1aSopenharmony_ci 137cabdff1aSopenharmony_ci // force null-termination, so in case our destination buffer is 138cabdff1aSopenharmony_ci // too small, the return value is larger than bufsize minus null. 139cabdff1aSopenharmony_ci if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) { 140cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n"); 141cabdff1aSopenharmony_ci return AVERROR_BUFFER_TOO_SMALL; 142cabdff1aSopenharmony_ci } 143cabdff1aSopenharmony_ci 144cabdff1aSopenharmony_ci return s->buffer.len; 145cabdff1aSopenharmony_ci} 146cabdff1aSopenharmony_ci 147cabdff1aSopenharmony_cistatic av_cold int ttml_encode_close(AVCodecContext *avctx) 148cabdff1aSopenharmony_ci{ 149cabdff1aSopenharmony_ci TTMLContext *s = avctx->priv_data; 150cabdff1aSopenharmony_ci 151cabdff1aSopenharmony_ci ff_ass_split_free(s->ass_ctx); 152cabdff1aSopenharmony_ci 153cabdff1aSopenharmony_ci av_bprint_finalize(&s->buffer, NULL); 154cabdff1aSopenharmony_ci 155cabdff1aSopenharmony_ci return 0; 156cabdff1aSopenharmony_ci} 157cabdff1aSopenharmony_ci 158cabdff1aSopenharmony_cistatic const char *ttml_get_display_alignment(int alignment) 159cabdff1aSopenharmony_ci{ 160cabdff1aSopenharmony_ci switch (alignment) { 161cabdff1aSopenharmony_ci case 1: 162cabdff1aSopenharmony_ci case 2: 163cabdff1aSopenharmony_ci case 3: 164cabdff1aSopenharmony_ci return "after"; 165cabdff1aSopenharmony_ci case 4: 166cabdff1aSopenharmony_ci case 5: 167cabdff1aSopenharmony_ci case 6: 168cabdff1aSopenharmony_ci return "center"; 169cabdff1aSopenharmony_ci case 7: 170cabdff1aSopenharmony_ci case 8: 171cabdff1aSopenharmony_ci case 9: 172cabdff1aSopenharmony_ci return "before"; 173cabdff1aSopenharmony_ci default: 174cabdff1aSopenharmony_ci return NULL; 175cabdff1aSopenharmony_ci } 176cabdff1aSopenharmony_ci} 177cabdff1aSopenharmony_ci 178cabdff1aSopenharmony_cistatic const char *ttml_get_text_alignment(int alignment) 179cabdff1aSopenharmony_ci{ 180cabdff1aSopenharmony_ci switch (alignment) { 181cabdff1aSopenharmony_ci case 1: 182cabdff1aSopenharmony_ci case 4: 183cabdff1aSopenharmony_ci case 7: 184cabdff1aSopenharmony_ci return "left"; 185cabdff1aSopenharmony_ci case 2: 186cabdff1aSopenharmony_ci case 5: 187cabdff1aSopenharmony_ci case 8: 188cabdff1aSopenharmony_ci return "center"; 189cabdff1aSopenharmony_ci case 3: 190cabdff1aSopenharmony_ci case 6: 191cabdff1aSopenharmony_ci case 9: 192cabdff1aSopenharmony_ci return "right"; 193cabdff1aSopenharmony_ci default: 194cabdff1aSopenharmony_ci return NULL; 195cabdff1aSopenharmony_ci } 196cabdff1aSopenharmony_ci} 197cabdff1aSopenharmony_ci 198cabdff1aSopenharmony_cistatic void ttml_get_origin(ASSScriptInfo script_info, ASSStyle style, 199cabdff1aSopenharmony_ci int *origin_left, int *origin_top) 200cabdff1aSopenharmony_ci{ 201cabdff1aSopenharmony_ci *origin_left = av_rescale(style.margin_l, 100, script_info.play_res_x); 202cabdff1aSopenharmony_ci *origin_top = 203cabdff1aSopenharmony_ci av_rescale((style.alignment >= 7) ? style.margin_v : 0, 204cabdff1aSopenharmony_ci 100, script_info.play_res_y); 205cabdff1aSopenharmony_ci} 206cabdff1aSopenharmony_ci 207cabdff1aSopenharmony_cistatic void ttml_get_extent(ASSScriptInfo script_info, ASSStyle style, 208cabdff1aSopenharmony_ci int *width, int *height) 209cabdff1aSopenharmony_ci{ 210cabdff1aSopenharmony_ci *width = av_rescale(script_info.play_res_x - style.margin_r, 211cabdff1aSopenharmony_ci 100, script_info.play_res_x); 212cabdff1aSopenharmony_ci *height = av_rescale((style.alignment <= 3) ? 213cabdff1aSopenharmony_ci script_info.play_res_y - style.margin_v : 214cabdff1aSopenharmony_ci script_info.play_res_y, 215cabdff1aSopenharmony_ci 100, script_info.play_res_y); 216cabdff1aSopenharmony_ci} 217cabdff1aSopenharmony_ci 218cabdff1aSopenharmony_cistatic int ttml_write_region(AVCodecContext *avctx, AVBPrint *buf, 219cabdff1aSopenharmony_ci ASSScriptInfo script_info, ASSStyle style) 220cabdff1aSopenharmony_ci{ 221cabdff1aSopenharmony_ci const char *display_alignment = NULL; 222cabdff1aSopenharmony_ci const char *text_alignment = NULL; 223cabdff1aSopenharmony_ci int origin_left = 0; 224cabdff1aSopenharmony_ci int origin_top = 0; 225cabdff1aSopenharmony_ci int width = 0; 226cabdff1aSopenharmony_ci int height = 0; 227cabdff1aSopenharmony_ci 228cabdff1aSopenharmony_ci if (!style.name) { 229cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Subtitle style name not set!\n"); 230cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 231cabdff1aSopenharmony_ci } 232cabdff1aSopenharmony_ci 233cabdff1aSopenharmony_ci if (style.font_size < 0) { 234cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Invalid font size for TTML: %d!\n", 235cabdff1aSopenharmony_ci style.font_size); 236cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 237cabdff1aSopenharmony_ci } 238cabdff1aSopenharmony_ci 239cabdff1aSopenharmony_ci if (style.margin_l < 0 || style.margin_r < 0 || style.margin_v < 0) { 240cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 241cabdff1aSopenharmony_ci "One or more negative margin values in subtitle style: " 242cabdff1aSopenharmony_ci "left: %d, right: %d, vertical: %d!\n", 243cabdff1aSopenharmony_ci style.margin_l, style.margin_r, style.margin_v); 244cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 245cabdff1aSopenharmony_ci } 246cabdff1aSopenharmony_ci 247cabdff1aSopenharmony_ci display_alignment = ttml_get_display_alignment(style.alignment); 248cabdff1aSopenharmony_ci text_alignment = ttml_get_text_alignment(style.alignment); 249cabdff1aSopenharmony_ci if (!display_alignment || !text_alignment) { 250cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 251cabdff1aSopenharmony_ci "Failed to convert ASS style alignment %d of style %s to " 252cabdff1aSopenharmony_ci "TTML display and text alignment!\n", 253cabdff1aSopenharmony_ci style.alignment, 254cabdff1aSopenharmony_ci style.name); 255cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 256cabdff1aSopenharmony_ci } 257cabdff1aSopenharmony_ci 258cabdff1aSopenharmony_ci ttml_get_origin(script_info, style, &origin_left, &origin_top); 259cabdff1aSopenharmony_ci ttml_get_extent(script_info, style, &width, &height); 260cabdff1aSopenharmony_ci 261cabdff1aSopenharmony_ci av_bprintf(buf, " <region xml:id=\""); 262cabdff1aSopenharmony_ci av_bprint_escape(buf, style.name, NULL, AV_ESCAPE_MODE_XML, 263cabdff1aSopenharmony_ci AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); 264cabdff1aSopenharmony_ci av_bprintf(buf, "\"\n"); 265cabdff1aSopenharmony_ci 266cabdff1aSopenharmony_ci av_bprintf(buf, " tts:origin=\"%d%% %d%%\"\n", 267cabdff1aSopenharmony_ci origin_left, origin_top); 268cabdff1aSopenharmony_ci av_bprintf(buf, " tts:extent=\"%d%% %d%%\"\n", 269cabdff1aSopenharmony_ci width, height); 270cabdff1aSopenharmony_ci 271cabdff1aSopenharmony_ci av_bprintf(buf, " tts:displayAlign=\""); 272cabdff1aSopenharmony_ci av_bprint_escape(buf, display_alignment, NULL, AV_ESCAPE_MODE_XML, 273cabdff1aSopenharmony_ci AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); 274cabdff1aSopenharmony_ci av_bprintf(buf, "\"\n"); 275cabdff1aSopenharmony_ci 276cabdff1aSopenharmony_ci av_bprintf(buf, " tts:textAlign=\""); 277cabdff1aSopenharmony_ci av_bprint_escape(buf, text_alignment, NULL, AV_ESCAPE_MODE_XML, 278cabdff1aSopenharmony_ci AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); 279cabdff1aSopenharmony_ci av_bprintf(buf, "\"\n"); 280cabdff1aSopenharmony_ci 281cabdff1aSopenharmony_ci // if we set cell resolution to our script reference resolution, 282cabdff1aSopenharmony_ci // then a single line is a single "point" on our canvas. Thus, by setting 283cabdff1aSopenharmony_ci // our font size to font size in cells, we should gain a similar enough 284cabdff1aSopenharmony_ci // scale without resorting to explicit pixel based font sizing, which is 285cabdff1aSopenharmony_ci // frowned upon in the TTML community. 286cabdff1aSopenharmony_ci av_bprintf(buf, " tts:fontSize=\"%dc\"\n", 287cabdff1aSopenharmony_ci style.font_size); 288cabdff1aSopenharmony_ci 289cabdff1aSopenharmony_ci if (style.font_name) { 290cabdff1aSopenharmony_ci av_bprintf(buf, " tts:fontFamily=\""); 291cabdff1aSopenharmony_ci av_bprint_escape(buf, style.font_name, NULL, AV_ESCAPE_MODE_XML, 292cabdff1aSopenharmony_ci AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); 293cabdff1aSopenharmony_ci av_bprintf(buf, "\"\n"); 294cabdff1aSopenharmony_ci } 295cabdff1aSopenharmony_ci 296cabdff1aSopenharmony_ci av_bprintf(buf, " tts:overflow=\"visible\" />\n"); 297cabdff1aSopenharmony_ci 298cabdff1aSopenharmony_ci return 0; 299cabdff1aSopenharmony_ci} 300cabdff1aSopenharmony_ci 301cabdff1aSopenharmony_cistatic int ttml_write_header_content(AVCodecContext *avctx) 302cabdff1aSopenharmony_ci{ 303cabdff1aSopenharmony_ci TTMLContext *s = avctx->priv_data; 304cabdff1aSopenharmony_ci ASS *ass = (ASS *)s->ass_ctx; 305cabdff1aSopenharmony_ci ASSScriptInfo script_info = ass->script_info; 306cabdff1aSopenharmony_ci const size_t base_extradata_size = TTMLENC_EXTRADATA_SIGNATURE_SIZE + 1 + 307cabdff1aSopenharmony_ci AV_INPUT_BUFFER_PADDING_SIZE; 308cabdff1aSopenharmony_ci size_t additional_extradata_size = 0; 309cabdff1aSopenharmony_ci 310cabdff1aSopenharmony_ci if (script_info.play_res_x <= 0 || script_info.play_res_y <= 0) { 311cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 312cabdff1aSopenharmony_ci "Invalid subtitle reference resolution %dx%d!\n", 313cabdff1aSopenharmony_ci script_info.play_res_x, script_info.play_res_y); 314cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 315cabdff1aSopenharmony_ci } 316cabdff1aSopenharmony_ci 317cabdff1aSopenharmony_ci // write the first string in extradata, attributes in the base "tt" element. 318cabdff1aSopenharmony_ci av_bprintf(&s->buffer, ttml_default_namespacing); 319cabdff1aSopenharmony_ci // the cell resolution is in character cells, so not exactly 1:1 against 320cabdff1aSopenharmony_ci // a pixel based resolution, but as the tts:extent in the root 321cabdff1aSopenharmony_ci // "tt" element is frowned upon (and disallowed in the EBU-TT profile), 322cabdff1aSopenharmony_ci // we mimic the reference resolution by setting it as the cell resolution. 323cabdff1aSopenharmony_ci av_bprintf(&s->buffer, " ttp:cellResolution=\"%d %d\"\n", 324cabdff1aSopenharmony_ci script_info.play_res_x, script_info.play_res_y); 325cabdff1aSopenharmony_ci av_bprint_chars(&s->buffer, '\0', 1); 326cabdff1aSopenharmony_ci 327cabdff1aSopenharmony_ci // write the second string in extradata, head element containing the styles 328cabdff1aSopenharmony_ci av_bprintf(&s->buffer, " <head>\n"); 329cabdff1aSopenharmony_ci av_bprintf(&s->buffer, " <layout>\n"); 330cabdff1aSopenharmony_ci 331cabdff1aSopenharmony_ci for (int i = 0; i < ass->styles_count; i++) { 332cabdff1aSopenharmony_ci int ret = ttml_write_region(avctx, &s->buffer, script_info, 333cabdff1aSopenharmony_ci ass->styles[i]); 334cabdff1aSopenharmony_ci if (ret < 0) 335cabdff1aSopenharmony_ci return ret; 336cabdff1aSopenharmony_ci } 337cabdff1aSopenharmony_ci 338cabdff1aSopenharmony_ci av_bprintf(&s->buffer, " </layout>\n"); 339cabdff1aSopenharmony_ci av_bprintf(&s->buffer, " </head>\n"); 340cabdff1aSopenharmony_ci av_bprint_chars(&s->buffer, '\0', 1); 341cabdff1aSopenharmony_ci 342cabdff1aSopenharmony_ci if (!av_bprint_is_complete(&s->buffer)) { 343cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 344cabdff1aSopenharmony_ci } 345cabdff1aSopenharmony_ci 346cabdff1aSopenharmony_ci additional_extradata_size = s->buffer.len; 347cabdff1aSopenharmony_ci 348cabdff1aSopenharmony_ci if (!(avctx->extradata = 349cabdff1aSopenharmony_ci av_mallocz(base_extradata_size + additional_extradata_size))) { 350cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 351cabdff1aSopenharmony_ci } 352cabdff1aSopenharmony_ci 353cabdff1aSopenharmony_ci avctx->extradata_size = 354cabdff1aSopenharmony_ci TTMLENC_EXTRADATA_SIGNATURE_SIZE + additional_extradata_size; 355cabdff1aSopenharmony_ci memcpy(avctx->extradata, TTMLENC_EXTRADATA_SIGNATURE, 356cabdff1aSopenharmony_ci TTMLENC_EXTRADATA_SIGNATURE_SIZE); 357cabdff1aSopenharmony_ci 358cabdff1aSopenharmony_ci if (additional_extradata_size) 359cabdff1aSopenharmony_ci memcpy(avctx->extradata + TTMLENC_EXTRADATA_SIGNATURE_SIZE, 360cabdff1aSopenharmony_ci s->buffer.str, additional_extradata_size); 361cabdff1aSopenharmony_ci 362cabdff1aSopenharmony_ci av_bprint_clear(&s->buffer); 363cabdff1aSopenharmony_ci 364cabdff1aSopenharmony_ci return 0; 365cabdff1aSopenharmony_ci} 366cabdff1aSopenharmony_ci 367cabdff1aSopenharmony_cistatic av_cold int ttml_encode_init(AVCodecContext *avctx) 368cabdff1aSopenharmony_ci{ 369cabdff1aSopenharmony_ci TTMLContext *s = avctx->priv_data; 370cabdff1aSopenharmony_ci int ret = AVERROR_BUG; 371cabdff1aSopenharmony_ci s->avctx = avctx; 372cabdff1aSopenharmony_ci 373cabdff1aSopenharmony_ci av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); 374cabdff1aSopenharmony_ci 375cabdff1aSopenharmony_ci if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) { 376cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 377cabdff1aSopenharmony_ci } 378cabdff1aSopenharmony_ci 379cabdff1aSopenharmony_ci if ((ret = ttml_write_header_content(avctx)) < 0) { 380cabdff1aSopenharmony_ci return ret; 381cabdff1aSopenharmony_ci } 382cabdff1aSopenharmony_ci 383cabdff1aSopenharmony_ci return 0; 384cabdff1aSopenharmony_ci} 385cabdff1aSopenharmony_ci 386cabdff1aSopenharmony_ciconst FFCodec ff_ttml_encoder = { 387cabdff1aSopenharmony_ci .p.name = "ttml", 388cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("TTML subtitle"), 389cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_SUBTITLE, 390cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_TTML, 391cabdff1aSopenharmony_ci .priv_data_size = sizeof(TTMLContext), 392cabdff1aSopenharmony_ci .init = ttml_encode_init, 393cabdff1aSopenharmony_ci FF_CODEC_ENCODE_SUB_CB(ttml_encode_frame), 394cabdff1aSopenharmony_ci .close = ttml_encode_close, 395cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, 396cabdff1aSopenharmony_ci}; 397