1/* 2 * 3GPP TS 26.245 Timed Text encoder 3 * Copyright (c) 2012 Philip Langdale <philipl@overt.org> 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include <stdarg.h> 23#include "avcodec.h" 24#include "libavutil/opt.h" 25#include "libavutil/avstring.h" 26#include "libavutil/intreadwrite.h" 27#include "libavutil/mem.h" 28#include "libavutil/common.h" 29#include "ass_split.h" 30#include "ass.h" 31#include "bytestream.h" 32#include "codec_internal.h" 33 34#define STYLE_FLAG_BOLD (1<<0) 35#define STYLE_FLAG_ITALIC (1<<1) 36#define STYLE_FLAG_UNDERLINE (1<<2) 37#define STYLE_RECORD_SIZE 12 38#define SIZE_ADD 10 39 40#define STYL_BOX (1<<0) 41#define HLIT_BOX (1<<1) 42#define HCLR_BOX (1<<2) 43 44#define DEFAULT_STYLE_FONT_ID 0x01 45#define DEFAULT_STYLE_FONTSIZE 0x12 46#define DEFAULT_STYLE_COLOR 0xffffffff 47#define DEFAULT_STYLE_FLAG 0x00 48 49#define BGR_TO_RGB(c) (((c) & 0xff) << 16 | ((c) & 0xff00) | (((uint32_t)(c) >> 16) & 0xff)) 50#define FONTSIZE_SCALE(s,fs) ((fs) * (s)->font_scale_factor + 0.5) 51#define av_bprint_append_any(buf, data, size) av_bprint_append_data(buf, ((const char*)data), size) 52 53typedef struct { 54 uint16_t style_start; 55 uint16_t style_end; 56 uint8_t style_flag; 57 uint16_t style_fontID; 58 uint8_t style_fontsize; 59 uint32_t style_color; 60} StyleBox; 61 62typedef struct { 63 uint16_t start; 64 uint16_t end; 65} HighlightBox; 66 67typedef struct { 68 uint32_t color; 69} HilightcolorBox; 70 71typedef struct { 72 AVClass *class; 73 AVCodecContext *avctx; 74 75 ASSSplitContext *ass_ctx; 76 ASSStyle *ass_dialog_style; 77 StyleBox *style_attributes; 78 unsigned count; 79 unsigned style_attributes_bytes_allocated; 80 StyleBox style_attributes_temp; 81 AVBPrint buffer; 82 HighlightBox hlit; 83 HilightcolorBox hclr; 84 uint8_t box_flags; 85 StyleBox d; 86 uint16_t text_pos; 87 char **fonts; 88 int font_count; 89 double font_scale_factor; 90 int frame_height; 91} MovTextContext; 92 93typedef struct { 94 void (*encode)(MovTextContext *s); 95} Box; 96 97static void mov_text_cleanup(MovTextContext *s) 98{ 99 s->count = 0; 100 s->style_attributes_temp = s->d; 101} 102 103static void encode_styl(MovTextContext *s) 104{ 105 if ((s->box_flags & STYL_BOX) && s->count) { 106 uint8_t buf[12], *p = buf; 107 108 bytestream_put_be32(&p, s->count * STYLE_RECORD_SIZE + SIZE_ADD); 109 bytestream_put_be32(&p, MKBETAG('s','t','y','l')); 110 bytestream_put_be16(&p, s->count); 111 /*The above three attributes are hard coded for now 112 but will come from ASS style in the future*/ 113 av_bprint_append_any(&s->buffer, buf, 10); 114 for (unsigned j = 0; j < s->count; j++) { 115 const StyleBox *style = &s->style_attributes[j]; 116 117 p = buf; 118 bytestream_put_be16(&p, style->style_start); 119 bytestream_put_be16(&p, style->style_end); 120 bytestream_put_be16(&p, style->style_fontID); 121 bytestream_put_byte(&p, style->style_flag); 122 bytestream_put_byte(&p, style->style_fontsize); 123 bytestream_put_be32(&p, style->style_color); 124 125 av_bprint_append_any(&s->buffer, buf, 12); 126 } 127 } 128 mov_text_cleanup(s); 129} 130 131static void encode_hlit(MovTextContext *s) 132{ 133 if (s->box_flags & HLIT_BOX) { 134 uint8_t buf[12], *p = buf; 135 136 bytestream_put_be32(&p, 12); 137 bytestream_put_be32(&p, MKBETAG('h','l','i','t')); 138 bytestream_put_be16(&p, s->hlit.start); 139 bytestream_put_be16(&p, s->hlit.end); 140 141 av_bprint_append_any(&s->buffer, buf, 12); 142 } 143} 144 145static void encode_hclr(MovTextContext *s) 146{ 147 if (s->box_flags & HCLR_BOX) { 148 uint8_t buf[12], *p = buf; 149 150 bytestream_put_be32(&p, 12); 151 bytestream_put_be32(&p, MKBETAG('h','c','l','r')); 152 bytestream_put_be32(&p, s->hclr.color); 153 154 av_bprint_append_any(&s->buffer, buf, 12); 155 } 156} 157 158static const Box box_types[] = { 159 { encode_styl }, 160 { encode_hlit }, 161 { encode_hclr }, 162}; 163 164const static size_t box_count = FF_ARRAY_ELEMS(box_types); 165 166static int mov_text_encode_close(AVCodecContext *avctx) 167{ 168 MovTextContext *s = avctx->priv_data; 169 170 ff_ass_split_free(s->ass_ctx); 171 av_freep(&s->style_attributes); 172 av_freep(&s->fonts); 173 av_bprint_finalize(&s->buffer, NULL); 174 return 0; 175} 176 177static int encode_sample_description(AVCodecContext *avctx) 178{ 179 ASS *ass; 180 ASSStyle *style; 181 int i, j; 182 uint32_t back_color = 0; 183 int font_names_total_len = 0; 184 MovTextContext *s = avctx->priv_data; 185 uint8_t buf[30], *p = buf; 186 187 // 0x00, 0x00, 0x00, 0x00, // uint32_t displayFlags 188 // 0x01, // int8_t horizontal-justification 189 // 0xFF, // int8_t vertical-justification 190 // 0x00, 0x00, 0x00, 0x00, // uint8_t background-color-rgba[4] 191 // BoxRecord { 192 // 0x00, 0x00, // int16_t top 193 // 0x00, 0x00, // int16_t left 194 // 0x00, 0x00, // int16_t bottom 195 // 0x00, 0x00, // int16_t right 196 // }; 197 // StyleRecord { 198 // 0x00, 0x00, // uint16_t startChar 199 // 0x00, 0x00, // uint16_t endChar 200 // 0x00, 0x01, // uint16_t font-ID 201 // 0x00, // uint8_t face-style-flags 202 // 0x12, // uint8_t font-size 203 // 0xFF, 0xFF, 0xFF, 0xFF, // uint8_t text-color-rgba[4] 204 // }; 205 // FontTableBox { 206 // 0x00, 0x00, 0x00, 0x12, // uint32_t size 207 // 'f', 't', 'a', 'b', // uint8_t name[4] 208 // 0x00, 0x01, // uint16_t entry-count 209 // FontRecord { 210 // 0x00, 0x01, // uint16_t font-ID 211 // 0x05, // uint8_t font-name-length 212 // 'S', 'e', 'r', 'i', 'f',// uint8_t font[font-name-length] 213 // }; 214 // }; 215 216 // Populate sample description from ASS header 217 ass = (ASS*)s->ass_ctx; 218 // Compute font scaling factor based on (optionally) provided 219 // output video height and ASS script play_res_y 220 if (s->frame_height && ass->script_info.play_res_y) 221 s->font_scale_factor = (double)s->frame_height / ass->script_info.play_res_y; 222 else 223 s->font_scale_factor = 1; 224 225 style = ff_ass_style_get(s->ass_ctx, "Default"); 226 if (!style && ass->styles_count) { 227 style = &ass->styles[0]; 228 } 229 s->d.style_fontID = DEFAULT_STYLE_FONT_ID; 230 s->d.style_fontsize = DEFAULT_STYLE_FONTSIZE; 231 s->d.style_color = DEFAULT_STYLE_COLOR; 232 s->d.style_flag = DEFAULT_STYLE_FLAG; 233 if (style) { 234 s->d.style_fontsize = FONTSIZE_SCALE(s, style->font_size); 235 s->d.style_color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8 | 236 255 - ((uint32_t)style->primary_color >> 24); 237 s->d.style_flag = (!!style->bold * STYLE_FLAG_BOLD) | 238 (!!style->italic * STYLE_FLAG_ITALIC) | 239 (!!style->underline * STYLE_FLAG_UNDERLINE); 240 back_color = (BGR_TO_RGB(style->back_color & 0xffffff) << 8) | 241 (255 - ((uint32_t)style->back_color >> 24)); 242 } 243 244 bytestream_put_be32(&p, 0); // displayFlags 245 bytestream_put_be16(&p, 0x01FF); // horizontal/vertical justification (2x int8_t) 246 bytestream_put_be32(&p, back_color); 247 bytestream_put_be64(&p, 0); // BoxRecord - 4xint16_t: top, left, bottom, right 248 // StyleRecord { 249 bytestream_put_be16(&p, s->d.style_start); 250 bytestream_put_be16(&p, s->d.style_end); 251 bytestream_put_be16(&p, s->d.style_fontID); 252 bytestream_put_byte(&p, s->d.style_flag); 253 bytestream_put_byte(&p, s->d.style_fontsize); 254 bytestream_put_be32(&p, s->d.style_color); 255 // }; 256 av_bprint_append_any(&s->buffer, buf, 30); 257 258 // Build font table 259 // We can't build a complete font table since that would require 260 // scanning all dialogs first. But we can at least fill in what 261 // is avaiable in the ASS header 262 if (style && ass->styles_count) { 263 // Find unique font names 264 if (style->font_name) { 265 av_dynarray_add(&s->fonts, &s->font_count, style->font_name); 266 font_names_total_len += strlen(style->font_name); 267 } 268 for (i = 0; i < ass->styles_count; i++) { 269 int found = 0; 270 if (!ass->styles[i].font_name) 271 continue; 272 for (j = 0; j < s->font_count; j++) { 273 if (!strcmp(s->fonts[j], ass->styles[i].font_name)) { 274 found = 1; 275 break; 276 } 277 } 278 if (!found) { 279 av_dynarray_add(&s->fonts, &s->font_count, 280 ass->styles[i].font_name); 281 font_names_total_len += strlen(ass->styles[i].font_name); 282 } 283 } 284 } else 285 av_dynarray_add(&s->fonts, &s->font_count, (char*)"Serif"); 286 287 // FontTableBox { 288 p = buf; 289 bytestream_put_be32(&p, SIZE_ADD + 3 * s->font_count + font_names_total_len); // Size 290 bytestream_put_be32(&p, MKBETAG('f','t','a','b')); 291 bytestream_put_be16(&p, s->font_count); 292 293 av_bprint_append_any(&s->buffer, buf, 10); 294 // FontRecord { 295 for (i = 0; i < s->font_count; i++) { 296 size_t len = strlen(s->fonts[i]); 297 298 p = buf; 299 bytestream_put_be16(&p, i + 1); //fontID 300 bytestream_put_byte(&p, len); 301 302 av_bprint_append_any(&s->buffer, buf, 3); 303 av_bprint_append_any(&s->buffer, s->fonts[i], len); 304 } 305 // }; 306 // }; 307 308 if (!av_bprint_is_complete(&s->buffer)) { 309 return AVERROR(ENOMEM); 310 } 311 312 avctx->extradata_size = s->buffer.len; 313 avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); 314 if (!avctx->extradata) { 315 return AVERROR(ENOMEM); 316 } 317 318 memcpy(avctx->extradata, s->buffer.str, avctx->extradata_size); 319 av_bprint_clear(&s->buffer); 320 321 return 0; 322} 323 324static av_cold int mov_text_encode_init(AVCodecContext *avctx) 325{ 326 int ret; 327 MovTextContext *s = avctx->priv_data; 328 s->avctx = avctx; 329 330 av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); 331 332 s->ass_ctx = ff_ass_split(avctx->subtitle_header); 333 if (!s->ass_ctx) 334 return AVERROR_INVALIDDATA; 335 ret = encode_sample_description(avctx); 336 if (ret < 0) 337 return ret; 338 339 return 0; 340} 341 342// Start a new style box if needed 343static int mov_text_style_start(MovTextContext *s) 344{ 345 // there's an existing style entry 346 if (s->style_attributes_temp.style_start == s->text_pos) 347 // Still at same text pos, use same entry 348 return 1; 349 if (s->style_attributes_temp.style_flag != s->d.style_flag || 350 s->style_attributes_temp.style_color != s->d.style_color || 351 s->style_attributes_temp.style_fontID != s->d.style_fontID || 352 s->style_attributes_temp.style_fontsize != s->d.style_fontsize) { 353 StyleBox *tmp; 354 355 // last style != defaults, end the style entry and start a new one 356 if (s->count + 1 > FFMIN(SIZE_MAX / sizeof(*s->style_attributes), UINT16_MAX) || 357 !(tmp = av_fast_realloc(s->style_attributes, 358 &s->style_attributes_bytes_allocated, 359 (s->count + 1) * sizeof(*s->style_attributes)))) { 360 mov_text_cleanup(s); 361 av_bprint_clear(&s->buffer); 362 s->box_flags &= ~STYL_BOX; 363 return 0; 364 } 365 s->style_attributes = tmp; 366 s->style_attributes_temp.style_end = s->text_pos; 367 s->style_attributes[s->count++] = s->style_attributes_temp; 368 s->box_flags |= STYL_BOX; 369 s->style_attributes_temp = s->d; 370 s->style_attributes_temp.style_start = s->text_pos; 371 } else { // style entry matches defaults, drop entry 372 s->style_attributes_temp = s->d; 373 s->style_attributes_temp.style_start = s->text_pos; 374 } 375 return 1; 376} 377 378static uint8_t mov_text_style_to_flag(const char style) 379{ 380 uint8_t style_flag = 0; 381 382 switch (style){ 383 case 'b': 384 style_flag = STYLE_FLAG_BOLD; 385 break; 386 case 'i': 387 style_flag = STYLE_FLAG_ITALIC; 388 break; 389 case 'u': 390 style_flag = STYLE_FLAG_UNDERLINE; 391 break; 392 } 393 return style_flag; 394} 395 396static void mov_text_style_set(MovTextContext *s, uint8_t style_flags) 397{ 398 if (!((s->style_attributes_temp.style_flag & style_flags) ^ style_flags)) { 399 // setting flags that that are already set 400 return; 401 } 402 if (mov_text_style_start(s)) 403 s->style_attributes_temp.style_flag |= style_flags; 404} 405 406static void mov_text_style_cb(void *priv, const char style, int close) 407{ 408 MovTextContext *s = priv; 409 uint8_t style_flag = mov_text_style_to_flag(style); 410 411 if (!!(s->style_attributes_temp.style_flag & style_flag) != close) { 412 // setting flag that is already set 413 return; 414 } 415 if (mov_text_style_start(s)) { 416 if (!close) 417 s->style_attributes_temp.style_flag |= style_flag; 418 else 419 s->style_attributes_temp.style_flag &= ~style_flag; 420 } 421} 422 423static void mov_text_color_set(MovTextContext *s, uint32_t color) 424{ 425 if ((s->style_attributes_temp.style_color & 0xffffff00) == color) { 426 // color hasn't changed 427 return; 428 } 429 if (mov_text_style_start(s)) 430 s->style_attributes_temp.style_color = (color & 0xffffff00) | 431 (s->style_attributes_temp.style_color & 0xff); 432} 433 434static void mov_text_color_cb(void *priv, unsigned int color, unsigned int color_id) 435{ 436 MovTextContext *s = priv; 437 438 color = BGR_TO_RGB(color) << 8; 439 if (color_id == 1) { //primary color changes 440 mov_text_color_set(s, color); 441 } else if (color_id == 2) { //secondary color changes 442 if (!(s->box_flags & HCLR_BOX)) 443 // Highlight alpha not set yet, use current primary alpha 444 s->hclr.color = s->style_attributes_temp.style_color; 445 if (!(s->box_flags & HLIT_BOX) || s->hlit.start == s->text_pos) { 446 s->box_flags |= HCLR_BOX; 447 s->box_flags |= HLIT_BOX; 448 s->hlit.start = s->text_pos; 449 s->hclr.color = color | (s->hclr.color & 0xFF); 450 } 451 else //close tag 452 s->hlit.end = s->text_pos; 453 /* If there are more than one secondary color changes in ASS, 454 take start of first section and end of last section. Movtext 455 allows only one highlight box per sample. 456 */ 457 } 458 // Movtext does not support changes to other color_id (outline, background) 459} 460 461static void mov_text_alpha_set(MovTextContext *s, uint8_t alpha) 462{ 463 if ((s->style_attributes_temp.style_color & 0xff) == alpha) { 464 // color hasn't changed 465 return; 466 } 467 if (mov_text_style_start(s)) 468 s->style_attributes_temp.style_color = 469 (s->style_attributes_temp.style_color & 0xffffff00) | alpha; 470} 471 472static void mov_text_alpha_cb(void *priv, int alpha, int alpha_id) 473{ 474 MovTextContext *s = priv; 475 476 alpha = 255 - alpha; 477 if (alpha_id == 1) // primary alpha changes 478 mov_text_alpha_set(s, alpha); 479 else if (alpha_id == 2) { //secondary alpha changes 480 if (!(s->box_flags & HCLR_BOX)) 481 // Highlight color not set yet, use current primary color 482 s->hclr.color = s->style_attributes_temp.style_color; 483 if (!(s->box_flags & HLIT_BOX) || s->hlit.start == s->text_pos) { 484 s->box_flags |= HCLR_BOX; 485 s->box_flags |= HLIT_BOX; 486 s->hlit.start = s->text_pos; 487 s->hclr.color = (s->hclr.color & 0xffffff00) | alpha; 488 } 489 else //close tag 490 s->hlit.end = s->text_pos; 491 } 492 // Movtext does not support changes to other alpha_id (outline, background) 493} 494 495static uint16_t find_font_id(MovTextContext *s, const char *name) 496{ 497 if (!name) 498 return 1; 499 500 for (int i = 0; i < s->font_count; i++) { 501 if (!strcmp(name, s->fonts[i])) 502 return i + 1; 503 } 504 return 1; 505} 506 507static void mov_text_font_name_set(MovTextContext *s, const char *name) 508{ 509 int fontID = find_font_id(s, name); 510 if (s->style_attributes_temp.style_fontID == fontID) { 511 // color hasn't changed 512 return; 513 } 514 if (mov_text_style_start(s)) 515 s->style_attributes_temp.style_fontID = fontID; 516} 517 518static void mov_text_font_name_cb(void *priv, const char *name) 519{ 520 mov_text_font_name_set((MovTextContext*)priv, name); 521} 522 523static void mov_text_font_size_set(MovTextContext *s, int size) 524{ 525 size = FONTSIZE_SCALE(s, size); 526 if (s->style_attributes_temp.style_fontsize == size) { 527 // color hasn't changed 528 return; 529 } 530 if (mov_text_style_start(s)) 531 s->style_attributes_temp.style_fontsize = size; 532} 533 534static void mov_text_font_size_cb(void *priv, int size) 535{ 536 mov_text_font_size_set((MovTextContext*)priv, size); 537} 538 539static void mov_text_end_cb(void *priv) 540{ 541 // End of text, close any open style record 542 mov_text_style_start((MovTextContext*)priv); 543} 544 545static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style) 546{ 547 uint8_t style_flags, alpha; 548 uint32_t color; 549 550 if (style) { 551 style_flags = (!!style->bold * STYLE_FLAG_BOLD) | 552 (!!style->italic * STYLE_FLAG_ITALIC) | 553 (!!style->underline * STYLE_FLAG_UNDERLINE); 554 mov_text_style_set(s, style_flags); 555 color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8; 556 mov_text_color_set(s, color); 557 alpha = 255 - ((uint32_t)style->primary_color >> 24); 558 mov_text_alpha_set(s, alpha); 559 mov_text_font_size_set(s, style->font_size); 560 mov_text_font_name_set(s, style->font_name); 561 } else { 562 // End current style record, go back to defaults 563 mov_text_style_start(s); 564 } 565} 566 567static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog) 568{ 569 ASSStyle *style = ff_ass_style_get(s->ass_ctx, dialog->style); 570 571 s->ass_dialog_style = style; 572 mov_text_ass_style_set(s, style); 573} 574 575static void mov_text_cancel_overrides_cb(void *priv, const char *style_name) 576{ 577 MovTextContext *s = priv; 578 ASSStyle *style; 579 580 if (!style_name || !*style_name) 581 style = s->ass_dialog_style; 582 else 583 style= ff_ass_style_get(s->ass_ctx, style_name); 584 585 mov_text_ass_style_set(s, style); 586} 587 588static unsigned utf8_strlen(const char *text, int len) 589{ 590 unsigned i = 0, ret = 0; 591 while (i < len) { 592 char c = text[i]; 593 if ((c & 0x80) == 0) 594 i += 1; 595 else if ((c & 0xE0) == 0xC0) 596 i += 2; 597 else if ((c & 0xF0) == 0xE0) 598 i += 3; 599 else if ((c & 0xF8) == 0xF0) 600 i += 4; 601 else 602 return 0; 603 ret++; 604 } 605 return ret; 606} 607 608static void mov_text_text_cb(void *priv, const char *text, int len) 609{ 610 unsigned utf8_len = utf8_strlen(text, len); 611 MovTextContext *s = priv; 612 av_bprint_append_data(&s->buffer, text, len); 613 // If it's not utf-8, just use the byte length 614 s->text_pos += utf8_len ? utf8_len : len; 615} 616 617static void mov_text_new_line_cb(void *priv, int forced) 618{ 619 MovTextContext *s = priv; 620 s->text_pos += 1; 621 av_bprint_chars(&s->buffer, '\n', 1); 622} 623 624static const ASSCodesCallbacks mov_text_callbacks = { 625 .text = mov_text_text_cb, 626 .new_line = mov_text_new_line_cb, 627 .style = mov_text_style_cb, 628 .color = mov_text_color_cb, 629 .alpha = mov_text_alpha_cb, 630 .font_name = mov_text_font_name_cb, 631 .font_size = mov_text_font_size_cb, 632 .cancel_overrides = mov_text_cancel_overrides_cb, 633 .end = mov_text_end_cb, 634}; 635 636static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, 637 int bufsize, const AVSubtitle *sub) 638{ 639 MovTextContext *s = avctx->priv_data; 640 ASSDialog *dialog; 641 int i, length; 642 643 s->text_pos = 0; 644 s->count = 0; 645 s->box_flags = 0; 646 av_bprint_clear(&s->buffer); 647 for (i = 0; i < sub->num_rects; i++) { 648 const char *ass = sub->rects[i]->ass; 649 650 if (sub->rects[i]->type != SUBTITLE_ASS) { 651 av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); 652 return AVERROR(EINVAL); 653 } 654 655 dialog = ff_ass_split_dialog(s->ass_ctx, ass); 656 if (!dialog) 657 return AVERROR(ENOMEM); 658 mov_text_dialog(s, dialog); 659 ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); 660 ff_ass_free_dialog(&dialog); 661 } 662 663 if (s->buffer.len > UINT16_MAX) 664 return AVERROR(ERANGE); 665 AV_WB16(buf, s->buffer.len); 666 buf += 2; 667 668 for (size_t j = 0; j < box_count; j++) 669 box_types[j].encode(s); 670 671 if (!av_bprint_is_complete(&s->buffer)) 672 return AVERROR(ENOMEM); 673 674 if (!s->buffer.len) 675 return 0; 676 677 if (s->buffer.len > bufsize - 3) { 678 av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); 679 return AVERROR_BUFFER_TOO_SMALL; 680 } 681 682 memcpy(buf, s->buffer.str, s->buffer.len); 683 length = s->buffer.len + 2; 684 685 return length; 686} 687 688#define OFFSET(x) offsetof(MovTextContext, x) 689#define FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_SUBTITLE_PARAM 690static const AVOption options[] = { 691 { "height", "Frame height, usually video height", OFFSET(frame_height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, 692 { NULL }, 693}; 694 695static const AVClass mov_text_encoder_class = { 696 .class_name = "MOV text enoder", 697 .item_name = av_default_item_name, 698 .option = options, 699 .version = LIBAVUTIL_VERSION_INT, 700}; 701 702const FFCodec ff_movtext_encoder = { 703 .p.name = "mov_text", 704 .p.long_name = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"), 705 .p.type = AVMEDIA_TYPE_SUBTITLE, 706 .p.id = AV_CODEC_ID_MOV_TEXT, 707 .priv_data_size = sizeof(MovTextContext), 708 .p.priv_class = &mov_text_encoder_class, 709 .init = mov_text_encode_init, 710 FF_CODEC_ENCODE_SUB_CB(mov_text_encode_frame), 711 .close = mov_text_encode_close, 712 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, 713}; 714