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