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