GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavcodec/movtextenc.c Lines: 205 340 60.3 %
Date: 2021-04-20 15:25:36 Branches: 61 160 38.1 %

Line Branch Exec Source
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
99
3
static void mov_text_cleanup(MovTextContext *s)
100
{
101
3
    s->count = 0;
102
3
    s->style_attributes_temp = s->d;
103
3
}
104
105
3
static void encode_styl(MovTextContext *s)
106
{
107

3
    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
3
    mov_text_cleanup(s);
131
3
}
132
133
3
static void encode_hlit(MovTextContext *s)
134
{
135
3
    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
3
}
146
147
3
static void encode_hclr(MovTextContext *s)
148
{
149
3
    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
3
}
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
168
1
static int mov_text_encode_close(AVCodecContext *avctx)
169
{
170
1
    MovTextContext *s = avctx->priv_data;
171
172
1
    ff_ass_split_free(s->ass_ctx);
173
1
    av_freep(&s->style_attributes);
174
1
    av_freep(&s->fonts);
175
1
    av_bprint_finalize(&s->buffer, NULL);
176
1
    return 0;
177
}
178
179
1
static int encode_sample_description(AVCodecContext *avctx)
180
{
181
    ASS *ass;
182
    ASSStyle *style;
183
    int i, j;
184
1
    uint32_t back_color = 0;
185
1
    int font_names_total_len = 0;
186
1
    MovTextContext *s = avctx->priv_data;
187
1
    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
1
    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

1
    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
1
        s->font_scale_factor = 1;
226
227
1
    style = ff_ass_style_get(s->ass_ctx, "Default");
228

1
    if (!style && ass->styles_count) {
229
        style = &ass->styles[0];
230
    }
231
1
    s->d.style_fontID   = DEFAULT_STYLE_FONT_ID;
232
1
    s->d.style_fontsize = DEFAULT_STYLE_FONTSIZE;
233
1
    s->d.style_color    = DEFAULT_STYLE_COLOR;
234
1
    s->d.style_flag     = DEFAULT_STYLE_FLAG;
235
1
    if (style) {
236
1
        s->d.style_fontsize = FONTSIZE_SCALE(s, style->font_size);
237
1
        s->d.style_color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8 |
238
1
                           255 - ((uint32_t)style->primary_color >> 24);
239
1
        s->d.style_flag = (!!style->bold      * STYLE_FLAG_BOLD)   |
240
2
                          (!!style->italic    * STYLE_FLAG_ITALIC) |
241
1
                          (!!style->underline * STYLE_FLAG_UNDERLINE);
242
1
        back_color = (BGR_TO_RGB(style->back_color & 0xffffff) << 8) |
243
1
                     (255 - ((uint32_t)style->back_color >> 24));
244
    }
245
246
1
    bytestream_put_be32(&p, 0); // displayFlags
247
1
    bytestream_put_be16(&p, 0x01FF); // horizontal/vertical justification (2x int8_t)
248
1
    bytestream_put_be32(&p, back_color);
249
1
    bytestream_put_be64(&p, 0); // BoxRecord - 4xint16_t: top, left, bottom, right
250
    //     StyleRecord {
251
1
    bytestream_put_be16(&p, s->d.style_start);
252
1
    bytestream_put_be16(&p, s->d.style_end);
253
1
    bytestream_put_be16(&p, s->d.style_fontID);
254
1
    bytestream_put_byte(&p, s->d.style_flag);
255
1
    bytestream_put_byte(&p, s->d.style_fontsize);
256
1
    bytestream_put_be32(&p, s->d.style_color);
257
    //     };
258
1
    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

1
    if (style && ass->styles_count) {
265
        // Find unique font names
266
1
        if (style->font_name) {
267
1
            av_dynarray_add(&s->fonts, &s->font_count, style->font_name);
268
1
            font_names_total_len += strlen(style->font_name);
269
        }
270
2
        for (i = 0; i < ass->styles_count; i++) {
271
1
            int found = 0;
272
1
            if (!ass->styles[i].font_name)
273
                continue;
274
1
            for (j = 0; j < s->font_count; j++) {
275
1
                if (!strcmp(s->fonts[j], ass->styles[i].font_name)) {
276
1
                    found = 1;
277
1
                    break;
278
                }
279
            }
280
1
            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
1
    p = buf;
291
1
    bytestream_put_be32(&p, SIZE_ADD + 3 * s->font_count + font_names_total_len); // Size
292
1
    bytestream_put_be32(&p, MKBETAG('f','t','a','b'));
293
1
    bytestream_put_be16(&p, s->font_count);
294
295
1
    av_bprint_append_any(&s->buffer, buf, 10);
296
    //     FontRecord {
297
2
    for (i = 0; i < s->font_count; i++) {
298
1
        size_t len = strlen(s->fonts[i]);
299
300
1
        p = buf;
301
1
        bytestream_put_be16(&p, i + 1); //fontID
302
1
        bytestream_put_byte(&p, len);
303
304
1
        av_bprint_append_any(&s->buffer, buf, 3);
305
1
        av_bprint_append_any(&s->buffer, s->fonts[i], len);
306
    }
307
    //     };
308
    //     };
309
310
1
    if (!av_bprint_is_complete(&s->buffer)) {
311
        return AVERROR(ENOMEM);
312
    }
313
314
1
    avctx->extradata_size = s->buffer.len;
315
1
    avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
316
1
    if (!avctx->extradata) {
317
        return AVERROR(ENOMEM);
318
    }
319
320
1
    memcpy(avctx->extradata, s->buffer.str, avctx->extradata_size);
321
1
    av_bprint_clear(&s->buffer);
322
323
1
    return 0;
324
}
325
326
1
static av_cold int mov_text_encode_init(AVCodecContext *avctx)
327
{
328
    int ret;
329
1
    MovTextContext *s = avctx->priv_data;
330
1
    s->avctx = avctx;
331
332
1
    av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
333
334
1
    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
335
1
    if (!s->ass_ctx)
336
        return AVERROR_INVALIDDATA;
337
1
    ret = encode_sample_description(avctx);
338
1
    if (ret < 0)
339
        return ret;
340
341
1
    return 0;
342
}
343
344
// Start a new style box if needed
345
7
static int mov_text_style_start(MovTextContext *s)
346
{
347
    // there's an existing style entry
348
7
    if (s->style_attributes_temp.style_start == s->text_pos)
349
        // Still at same text pos, use same entry
350
4
        return 1;
351
3
    if (s->style_attributes_temp.style_flag     != s->d.style_flag   ||
352
3
        s->style_attributes_temp.style_color    != s->d.style_color  ||
353
3
        s->style_attributes_temp.style_fontID   != s->d.style_fontID ||
354
3
        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
3
        s->style_attributes_temp = s->d;
375
3
        s->style_attributes_temp.style_start = s->text_pos;
376
    }
377
3
    return 1;
378
}
379
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
398
3
static void mov_text_style_set(MovTextContext *s, uint8_t style_flags)
399
{
400
3
    if (!((s->style_attributes_temp.style_flag & style_flags) ^ style_flags)) {
401
        // setting flags that that are already set
402
3
        return;
403
    }
404
    if (mov_text_style_start(s))
405
        s->style_attributes_temp.style_flag |= style_flags;
406
}
407
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
425
3
static void mov_text_color_set(MovTextContext *s, uint32_t color)
426
{
427
3
    if ((s->style_attributes_temp.style_color & 0xffffff00) == color) {
428
        // color hasn't changed
429
2
        return;
430
    }
431
1
    if (mov_text_style_start(s))
432
1
        s->style_attributes_temp.style_color = (color & 0xffffff00) |
433
1
                            (s->style_attributes_temp.style_color & 0xff);
434
}
435
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
463
3
static void mov_text_alpha_set(MovTextContext *s, uint8_t alpha)
464
{
465
3
    if ((s->style_attributes_temp.style_color & 0xff) == alpha) {
466
        // color hasn't changed
467
2
        return;
468
    }
469
1
    if (mov_text_style_start(s))
470
1
        s->style_attributes_temp.style_color =
471
1
                (s->style_attributes_temp.style_color & 0xffffff00) | alpha;
472
}
473
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
497
3
static uint16_t find_font_id(MovTextContext *s, const char *name)
498
{
499
    int i;
500
3
    for (i = 0; i < s->font_count; i++) {
501
3
        if (!strcmp(name, s->fonts[i]))
502
3
            return i + 1;
503
    }
504
    return 1;
505
}
506
507
3
static void mov_text_font_name_set(MovTextContext *s, const char *name)
508
{
509
3
    int fontID = find_font_id(s, name);
510
3
    if (s->style_attributes_temp.style_fontID == fontID) {
511
        // color hasn't changed
512
2
        return;
513
    }
514
1
    if (mov_text_style_start(s))
515
1
        s->style_attributes_temp.style_fontID = fontID;
516
}
517
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
523
3
static void mov_text_font_size_set(MovTextContext *s, int size)
524
{
525
3
    size = FONTSIZE_SCALE(s, size);
526
3
    if (s->style_attributes_temp.style_fontsize == size) {
527
        // color hasn't changed
528
2
        return;
529
    }
530
1
    if (mov_text_style_start(s))
531
1
        s->style_attributes_temp.style_fontsize = size;
532
}
533
534
static void mov_text_font_size_cb(void *priv, int size)
535
{
536
    mov_text_font_size_set((MovTextContext*)priv, size);
537
}
538
539
3
static void mov_text_end_cb(void *priv)
540
{
541
    // End of text, close any open style record
542
3
    mov_text_style_start((MovTextContext*)priv);
543
3
}
544
545
3
static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style)
546
{
547
    uint8_t    style_flags, alpha;
548
    uint32_t   color;
549
550
3
    if (style) {
551
3
        style_flags = (!!style->bold      * STYLE_FLAG_BOLD)   |
552
6
                      (!!style->italic    * STYLE_FLAG_ITALIC) |
553
3
                      (!!style->underline * STYLE_FLAG_UNDERLINE);
554
3
        mov_text_style_set(s, style_flags);
555
3
        color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8;
556
3
        mov_text_color_set(s, color);
557
3
        alpha = 255 - ((uint32_t)style->primary_color >> 24);
558
3
        mov_text_alpha_set(s, alpha);
559
3
        mov_text_font_size_set(s, style->font_size);
560
3
        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
3
}
566
567
3
static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog)
568
{
569
3
    ASSStyle *style = ff_ass_style_get(s->ass_ctx, dialog->style);
570
571
3
    s->ass_dialog_style = style;
572
3
    mov_text_ass_style_set(s, style);
573
3
}
574
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
588
5
static uint16_t utf8_strlen(const char *text, int len)
589
{
590
5
    uint16_t i = 0, ret = 0;
591
48
    while (i < len) {
592
43
        char c = text[i];
593
43
        if ((c & 0x80) == 0)
594
43
            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
43
        ret++;
604
    }
605
5
    return ret;
606
}
607
608
5
static void mov_text_text_cb(void *priv, const char *text, int len)
609
{
610
5
    uint16_t utf8_len = utf8_strlen(text, len);
611
5
    MovTextContext *s = priv;
612
5
    av_bprint_append_data(&s->buffer, text, len);
613
    // If it's not utf-8, just use the byte length
614
5
    s->text_pos += utf8_len ? utf8_len : len;
615
5
    s->byte_count += len;
616
5
}
617
618
2
static void mov_text_new_line_cb(void *priv, int forced)
619
{
620
2
    MovTextContext *s = priv;
621
2
    av_bprint_append_data(&s->buffer, "\n", 1);
622
2
    s->text_pos += 1;
623
2
    s->byte_count += 1;
624
2
}
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
638
3
static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
639
                                 int bufsize, const AVSubtitle *sub)
640
{
641
3
    MovTextContext *s = avctx->priv_data;
642
    ASSDialog *dialog;
643
    int i, length;
644
    size_t j;
645
646
3
    s->byte_count = 0;
647
3
    s->text_pos = 0;
648
3
    s->count = 0;
649
3
    s->box_flags = 0;
650
6
    for (i = 0; i < sub->num_rects; i++) {
651
3
        const char *ass = sub->rects[i]->ass;
652
653
3
        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
3
        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
3
            dialog = ff_ass_split_dialog2(s->ass_ctx, ass);
669
3
            if (!dialog)
670
                return AVERROR(ENOMEM);
671
3
            mov_text_dialog(s, dialog);
672
3
            ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
673
3
            ff_ass_free_dialog(&dialog);
674
#if FF_API_ASS_TIMING
675
        }
676
#endif
677
678
12
        for (j = 0; j < box_count; j++) {
679
9
            box_types[j].encode(s);
680
        }
681
    }
682
683
3
    AV_WB16(buf, s->byte_count);
684
3
    buf += 2;
685
686
3
    if (!av_bprint_is_complete(&s->buffer)) {
687
        length = AVERROR(ENOMEM);
688
        goto exit;
689
    }
690
691
3
    if (!s->buffer.len) {
692
        length = 0;
693
        goto exit;
694
    }
695
696
3
    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
3
    memcpy(buf, s->buffer.str, s->buffer.len);
703
3
    length = s->buffer.len + 2;
704
705
3
exit:
706
3
    av_bprint_clear(&s->buffer);
707
3
    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
};