• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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