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