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