GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavcodec/movtextenc.c Lines: 215 365 58.9 %
Date: 2020-07-13 04:22:34 Branches: 68 178 38.2 %

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
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
3
static void mov_text_cleanup(MovTextContext *s)
98
{
99
    int j;
100
3
    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
3
    if (s->style_attributes_temp) {
107
3
        *s->style_attributes_temp = s->d;
108
    }
109
3
}
110
111
3
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

3
    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
        style_entries = AV_RB16(&s->count);
120
        /*The above three attributes are hard coded for now
121
        but will come from ASS style in the future*/
122
        av_bprint_append_any(&s->buffer, &tsmb_size, 4);
123
        av_bprint_append_any(&s->buffer, &tsmb_type, 4);
124
        av_bprint_append_any(&s->buffer, &style_entries, 2);
125
        for (j = 0; j < s->count; j++) {
126
            uint16_t style_start, style_end, style_fontID;
127
            uint32_t style_color;
128
129
            style_start  = AV_RB16(&s->style_attributes[j]->style_start);
130
            style_end    = AV_RB16(&s->style_attributes[j]->style_end);
131
            style_color  = AV_RB32(&s->style_attributes[j]->style_color);
132
            style_fontID = AV_RB16(&s->style_attributes[j]->style_fontID);
133
134
            av_bprint_append_any(&s->buffer, &style_start, 2);
135
            av_bprint_append_any(&s->buffer, &style_end, 2);
136
            av_bprint_append_any(&s->buffer, &style_fontID, 2);
137
            av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_flag, 1);
138
            av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_fontsize, 1);
139
            av_bprint_append_any(&s->buffer, &style_color, 4);
140
        }
141
    }
142
3
    mov_text_cleanup(s);
143
3
}
144
145
3
static void encode_hlit(MovTextContext *s, uint32_t tsmb_type)
146
{
147
    uint32_t tsmb_size;
148
    uint16_t start, end;
149
3
    if (s->box_flags & HLIT_BOX) {
150
        tsmb_size = 12;
151
        tsmb_size = AV_RB32(&tsmb_size);
152
        start     = AV_RB16(&s->hlit.start);
153
        end       = AV_RB16(&s->hlit.end);
154
        av_bprint_append_any(&s->buffer, &tsmb_size, 4);
155
        av_bprint_append_any(&s->buffer, &tsmb_type, 4);
156
        av_bprint_append_any(&s->buffer, &start, 2);
157
        av_bprint_append_any(&s->buffer, &end, 2);
158
    }
159
3
}
160
161
3
static void encode_hclr(MovTextContext *s, uint32_t tsmb_type)
162
{
163
    uint32_t tsmb_size, color;
164
3
    if (s->box_flags & HCLR_BOX) {
165
        tsmb_size = 12;
166
        tsmb_size = AV_RB32(&tsmb_size);
167
        color     = AV_RB32(&s->hclr.color);
168
        av_bprint_append_any(&s->buffer, &tsmb_size, 4);
169
        av_bprint_append_any(&s->buffer, &tsmb_type, 4);
170
        av_bprint_append_any(&s->buffer, &color, 4);
171
    }
172
3
}
173
174
static const Box box_types[] = {
175
    { MKTAG('s','t','y','l'), encode_styl },
176
    { MKTAG('h','l','i','t'), encode_hlit },
177
    { MKTAG('h','c','l','r'), encode_hclr },
178
};
179
180
const static size_t box_count = FF_ARRAY_ELEMS(box_types);
181
182
1
static int mov_text_encode_close(AVCodecContext *avctx)
183
{
184
1
    MovTextContext *s = avctx->priv_data;
185
    int i;
186
187
1
    ff_ass_split_free(s->ass_ctx);
188
1
    if (s->style_attributes) {
189
        for (i = 0; i < s->count; i++) {
190
            av_freep(&s->style_attributes[i]);
191
        }
192
        av_freep(&s->style_attributes);
193
    }
194
1
    av_freep(&s->fonts);
195
1
    av_freep(&s->style_attributes_temp);
196
1
    av_bprint_finalize(&s->buffer, NULL);
197
1
    return 0;
198
}
199
200
1
static int encode_sample_description(AVCodecContext *avctx)
201
{
202
    ASS * ass;
203
    ASSStyle * style;
204
    int i, j;
205
    uint32_t tsmb_size, tsmb_type, back_color, style_color;
206
    uint16_t style_start, style_end, fontID, count;
207
1
    int font_names_total_len = 0;
208
1
    MovTextContext *s = avctx->priv_data;
209
210
    static const uint8_t display_and_justification[] = {
211
        0x00, 0x00, 0x00, 0x00, // uint32_t displayFlags
212
        0x01,                   // int8_t horizontal-justification
213
        0xFF,                   // int8_t vertical-justification
214
    };
215
    //  0x00, 0x00, 0x00, 0x00, // uint8_t background-color-rgba[4]
216
    static const uint8_t box_record[] = {
217
    //     BoxRecord {
218
        0x00, 0x00,             // int16_t top
219
        0x00, 0x00,             // int16_t left
220
        0x00, 0x00,             // int16_t bottom
221
        0x00, 0x00,             // int16_t right
222
    //     };
223
    };
224
    //     StyleRecord {
225
    //  0x00, 0x00,             // uint16_t startChar
226
    //  0x00, 0x00,             // uint16_t endChar
227
    //  0x00, 0x01,             // uint16_t font-ID
228
    //  0x00,                   // uint8_t face-style-flags
229
    //  0x12,                   // uint8_t font-size
230
    //  0xFF, 0xFF, 0xFF, 0xFF, // uint8_t text-color-rgba[4]
231
    //     };
232
    //     FontTableBox {
233
    //  0x00, 0x00, 0x00, 0x12, // uint32_t size
234
    //  'f', 't', 'a', 'b',     // uint8_t name[4]
235
    //  0x00, 0x01,             // uint16_t entry-count
236
    //     FontRecord {
237
    //  0x00, 0x01,             // uint16_t font-ID
238
    //  0x05,                   // uint8_t font-name-length
239
    //  'S', 'e', 'r', 'i', 'f',// uint8_t font[font-name-length]
240
    //     };
241
    //     };
242
243
    // Populate sample description from ASS header
244
1
    ass = (ASS*)s->ass_ctx;
245
    // Compute font scaling factor based on (optionally) provided
246
    // output video height and ASS script play_res_y
247

1
    if (s->frame_height && ass->script_info.play_res_y)
248
        s->font_scale_factor = (double)s->frame_height / ass->script_info.play_res_y;
249
    else
250
1
        s->font_scale_factor = 1;
251
252
1
    style = ff_ass_style_get(s->ass_ctx, "Default");
253

1
    if (!style && ass->styles_count) {
254
        style = &ass->styles[0];
255
    }
256
1
    s->d.style_fontID   = DEFAULT_STYLE_FONT_ID;
257
1
    s->d.style_fontsize = DEFAULT_STYLE_FONTSIZE;
258
1
    s->d.style_color    = DEFAULT_STYLE_COLOR;
259
1
    s->d.style_flag     = DEFAULT_STYLE_FLAG;
260
1
    if (style) {
261
1
        s->d.style_fontsize = FONTSIZE_SCALE(s, style->font_size);
262
1
        s->d.style_color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8 |
263
1
                           255 - ((uint32_t)style->primary_color >> 24);
264
1
        s->d.style_flag = (!!style->bold      * STYLE_FLAG_BOLD)   |
265
2
                          (!!style->italic    * STYLE_FLAG_ITALIC) |
266
1
                          (!!style->underline * STYLE_FLAG_UNDERLINE);
267
1
        back_color = (BGR_TO_RGB(style->back_color & 0xffffff) << 8) |
268
1
                     (255 - ((uint32_t)style->back_color >> 24));
269
    }
270
271
1
    av_bprint_append_any(&s->buffer, display_and_justification,
272
                                     sizeof(display_and_justification));
273
1
    back_color = AV_RB32(&back_color);
274
1
    av_bprint_append_any(&s->buffer, &back_color, 4);
275
    //     BoxRecord {
276
1
    av_bprint_append_any(&s->buffer, box_record, sizeof(box_record));
277
    //     };
278
    //     StyleRecord {
279
1
    style_start  = AV_RB16(&s->d.style_start);
280
1
    style_end    = AV_RB16(&s->d.style_end);
281
1
    fontID = AV_RB16(&s->d.style_fontID);
282
1
    style_color  = AV_RB32(&s->d.style_color);
283
1
    av_bprint_append_any(&s->buffer, &style_start, 2);
284
1
    av_bprint_append_any(&s->buffer, &style_end, 2);
285
1
    av_bprint_append_any(&s->buffer, &fontID, 2);
286
1
    av_bprint_append_any(&s->buffer, &s->d.style_flag, 1);
287
1
    av_bprint_append_any(&s->buffer, &s->d.style_fontsize, 1);
288
1
    av_bprint_append_any(&s->buffer, &style_color, 4);
289
    //     };
290
291
    // Build font table
292
    // We can't build a complete font table since that would require
293
    // scanning all dialogs first.  But we can at least fill in what
294
    // is avaiable in the ASS header
295

1
    if (style && ass->styles_count) {
296
        // Find unique font names
297
1
        av_dynarray_add(&s->fonts, &s->font_count, style->font_name);
298
1
        font_names_total_len += strlen(style->font_name);
299
2
        for (i = 0; i < ass->styles_count; i++) {
300
1
            int found = 0;
301
1
            for (j = 0; j < s->font_count; j++) {
302
1
                if (!strcmp(s->fonts[j], ass->styles[i].font_name)) {
303
1
                    found = 1;
304
1
                    break;
305
                }
306
            }
307
1
            if (!found) {
308
                av_dynarray_add(&s->fonts, &s->font_count,
309
                                           ass->styles[i].font_name);
310
                font_names_total_len += strlen(ass->styles[i].font_name);
311
            }
312
        }
313
    } else
314
        av_dynarray_add(&s->fonts, &s->font_count, (char*)"Serif");
315
316
    //     FontTableBox {
317
1
    tsmb_size = SIZE_ADD + 3 * s->font_count + font_names_total_len;
318
1
    tsmb_size = AV_RB32(&tsmb_size);
319
1
    tsmb_type = MKTAG('f','t','a','b');
320
1
    count = AV_RB16(&s->font_count);
321
1
    av_bprint_append_any(&s->buffer, &tsmb_size, 4);
322
1
    av_bprint_append_any(&s->buffer, &tsmb_type, 4);
323
1
    av_bprint_append_any(&s->buffer, &count, 2);
324
    //     FontRecord {
325
2
    for (i = 0; i < s->font_count; i++) {
326
        int len;
327
1
        fontID = i + 1;
328
1
        fontID = AV_RB16(&fontID);
329
1
        av_bprint_append_any(&s->buffer, &fontID, 2);
330
1
        len = strlen(s->fonts[i]);
331
1
        av_bprint_append_any(&s->buffer, &len, 1);
332
1
        av_bprint_append_any(&s->buffer, s->fonts[i], len);
333
    }
334
    //     };
335
    //     };
336
337
1
    if (!av_bprint_is_complete(&s->buffer)) {
338
        return AVERROR(ENOMEM);
339
    }
340
341
1
    avctx->extradata_size = s->buffer.len;
342
1
    avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
343
1
    if (!avctx->extradata) {
344
        return AVERROR(ENOMEM);
345
    }
346
347
1
    memcpy(avctx->extradata, s->buffer.str, avctx->extradata_size);
348
1
    av_bprint_clear(&s->buffer);
349
350
1
    return 0;
351
}
352
353
1
static av_cold int mov_text_encode_init(AVCodecContext *avctx)
354
{
355
    int ret;
356
1
    MovTextContext *s = avctx->priv_data;
357
1
    s->avctx = avctx;
358
359
1
    av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
360
361
1
    s->style_attributes_temp = av_mallocz(sizeof(*s->style_attributes_temp));
362
1
    if (!s->style_attributes_temp) {
363
        ret = AVERROR(ENOMEM);
364
        goto fail;
365
    }
366
367
1
    s->ass_ctx = ff_ass_split(avctx->subtitle_header);
368
1
    if (!s->ass_ctx) {
369
        ret = AVERROR_INVALIDDATA;
370
        goto fail;
371
    }
372
1
    ret = encode_sample_description(avctx);
373
1
    if (ret < 0)
374
        goto fail;
375
376
1
    return 0;
377
378
fail:
379
    mov_text_encode_close(avctx);
380
    return ret;
381
}
382
383
// Start a new style box if needed
384
7
static int mov_text_style_start(MovTextContext *s)
385
{
386
    // there's an existing style entry
387
7
    if (s->style_attributes_temp->style_start == s->text_pos)
388
        // Still at same text pos, use same entry
389
4
        return 1;
390
3
    if (s->style_attributes_temp->style_flag     != s->d.style_flag   ||
391
3
        s->style_attributes_temp->style_color    != s->d.style_color  ||
392
3
        s->style_attributes_temp->style_fontID   != s->d.style_fontID ||
393
3
        s->style_attributes_temp->style_fontsize != s->d.style_fontsize) {
394
        // last style != defaults, end the style entry and start a new one
395
        s->box_flags |= STYL_BOX;
396
        s->style_attributes_temp->style_end = s->text_pos;
397
        av_dynarray_add(&s->style_attributes, &s->count, s->style_attributes_temp);
398
        s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp));
399
        if (!s->style_attributes_temp) {
400
            mov_text_cleanup(s);
401
            av_bprint_clear(&s->buffer);
402
            s->box_flags &= ~STYL_BOX;
403
            return 0;
404
        }
405
406
        *s->style_attributes_temp = s->d;
407
        s->style_attributes_temp->style_start = s->text_pos;
408
    } else { // style entry matches defaults, drop entry
409
3
        *s->style_attributes_temp = s->d;
410
3
        s->style_attributes_temp->style_start = s->text_pos;
411
    }
412
3
    return 1;
413
}
414
415
static uint8_t mov_text_style_to_flag(const char style)
416
{
417
    uint8_t style_flag = 0;
418
419
    switch (style){
420
    case 'b':
421
        style_flag = STYLE_FLAG_BOLD;
422
        break;
423
    case 'i':
424
        style_flag = STYLE_FLAG_ITALIC;
425
        break;
426
    case 'u':
427
        style_flag = STYLE_FLAG_UNDERLINE;
428
        break;
429
    }
430
    return style_flag;
431
}
432
433
3
static void mov_text_style_set(MovTextContext *s, uint8_t style_flags)
434
{
435
3
    if (!s->style_attributes_temp ||
436
3
        !((s->style_attributes_temp->style_flag & style_flags) ^ style_flags)) {
437
        // setting flags that that are already set
438
3
        return;
439
    }
440
    if (mov_text_style_start(s))
441
        s->style_attributes_temp->style_flag |= style_flags;
442
}
443
444
static void mov_text_style_cb(void *priv, const char style, int close)
445
{
446
    MovTextContext *s = priv;
447
    uint8_t style_flag = mov_text_style_to_flag(style);
448
449
    if (!s->style_attributes_temp ||
450
        !!(s->style_attributes_temp->style_flag & style_flag) != close) {
451
        // setting flag that is already set
452
        return;
453
    }
454
    if (mov_text_style_start(s)) {
455
        if (!close)
456
            s->style_attributes_temp->style_flag |= style_flag;
457
        else
458
            s->style_attributes_temp->style_flag &= ~style_flag;
459
    }
460
}
461
462
3
static void mov_text_color_set(MovTextContext *s, uint32_t color)
463
{
464
3
    if (!s->style_attributes_temp ||
465
3
        (s->style_attributes_temp->style_color & 0xffffff00) == color) {
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 = (color & 0xffffff00) |
471
1
                            (s->style_attributes_temp->style_color & 0xff);
472
}
473
474
static void mov_text_color_cb(void *priv, unsigned int color, unsigned int color_id)
475
{
476
    MovTextContext *s = priv;
477
478
    color = BGR_TO_RGB(color) << 8;
479
    if (color_id == 1) {    //primary color changes
480
        mov_text_color_set(s, color);
481
    } else if (color_id == 2) {    //secondary color changes
482
        if (!(s->box_flags & HCLR_BOX))
483
            // Highlight alpha not set yet, use current primary alpha
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 = color | (s->hclr.color & 0xFF);
490
        }
491
        else //close tag
492
            s->hlit.end = s->text_pos;
493
        /* If there are more than one secondary color changes in ASS,
494
           take start of first section and end of last section. Movtext
495
           allows only one highlight box per sample.
496
         */
497
    }
498
    // Movtext does not support changes to other color_id (outline, background)
499
}
500
501
3
static void mov_text_alpha_set(MovTextContext *s, uint8_t alpha)
502
{
503
3
    if (!s->style_attributes_temp ||
504
3
        (s->style_attributes_temp->style_color & 0xff) == alpha) {
505
        // color hasn't changed
506
2
        return;
507
    }
508
1
    if (mov_text_style_start(s))
509
1
        s->style_attributes_temp->style_color =
510
1
                (s->style_attributes_temp->style_color & 0xffffff00) | alpha;
511
}
512
513
static void mov_text_alpha_cb(void *priv, int alpha, int alpha_id)
514
{
515
    MovTextContext *s = priv;
516
517
    alpha = 255 - alpha;
518
    if (alpha_id == 1) // primary alpha changes
519
        mov_text_alpha_set(s, alpha);
520
    else if (alpha_id == 2) {    //secondary alpha changes
521
        if (!(s->box_flags & HCLR_BOX))
522
            // Highlight color not set yet, use current primary color
523
            s->hclr.color = s->style_attributes_temp->style_color;
524
        if (!(s->box_flags & HLIT_BOX) || s->hlit.start == s->text_pos) {
525
            s->box_flags |= HCLR_BOX;
526
            s->box_flags |= HLIT_BOX;
527
            s->hlit.start = s->text_pos;
528
            s->hclr.color = (s->hclr.color & 0xffffff00) | alpha;
529
        }
530
        else //close tag
531
            s->hlit.end = s->text_pos;
532
    }
533
    // Movtext does not support changes to other alpha_id (outline, background)
534
}
535
536
3
static uint16_t find_font_id(MovTextContext * s, const char * name)
537
{
538
    int i;
539
3
    for (i = 0; i < s->font_count; i++) {
540
3
        if (!strcmp(name, s->fonts[i]))
541
3
            return i + 1;
542
    }
543
    return 1;
544
}
545
546
3
static void mov_text_font_name_set(MovTextContext *s, const char *name)
547
{
548
3
    int fontID = find_font_id(s, name);
549
3
    if (!s->style_attributes_temp ||
550
3
        s->style_attributes_temp->style_fontID == fontID) {
551
        // color hasn't changed
552
2
        return;
553
    }
554
1
    if (mov_text_style_start(s))
555
1
        s->style_attributes_temp->style_fontID = fontID;
556
}
557
558
static void mov_text_font_name_cb(void *priv, const char *name)
559
{
560
    mov_text_font_name_set((MovTextContext*)priv, name);
561
}
562
563
3
static void mov_text_font_size_set(MovTextContext *s, int size)
564
{
565
3
    size = FONTSIZE_SCALE(s, size);
566
3
    if (!s->style_attributes_temp ||
567
3
        s->style_attributes_temp->style_fontsize == size) {
568
        // color hasn't changed
569
2
        return;
570
    }
571
1
    if (mov_text_style_start(s))
572
1
        s->style_attributes_temp->style_fontsize = size;
573
}
574
575
static void mov_text_font_size_cb(void *priv, int size)
576
{
577
    mov_text_font_size_set((MovTextContext*)priv, size);
578
}
579
580
3
static void mov_text_end_cb(void *priv)
581
{
582
    // End of text, close any open style record
583
3
    mov_text_style_start((MovTextContext*)priv);
584
3
}
585
586
3
static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style)
587
{
588
    uint8_t    style_flags, alpha;
589
    uint32_t   color;
590
591
3
    if (style) {
592
3
        style_flags = (!!style->bold      * STYLE_FLAG_BOLD)   |
593
6
                      (!!style->italic    * STYLE_FLAG_ITALIC) |
594
3
                      (!!style->underline * STYLE_FLAG_UNDERLINE);
595
3
        mov_text_style_set(s, style_flags);
596
3
        color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8;
597
3
        mov_text_color_set(s, color);
598
3
        alpha = 255 - ((uint32_t)style->primary_color >> 24);
599
3
        mov_text_alpha_set(s, alpha);
600
3
        mov_text_font_size_set(s, style->font_size);
601
3
        mov_text_font_name_set(s, style->font_name);
602
    } else {
603
        // End current style record, go back to defaults
604
        mov_text_style_start(s);
605
    }
606
3
}
607
608
3
static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog)
609
{
610
3
    ASSStyle * style = ff_ass_style_get(s->ass_ctx, dialog->style);
611
612
3
    s->ass_dialog_style = style;
613
3
    mov_text_ass_style_set(s, style);
614
3
}
615
616
static void mov_text_cancel_overrides_cb(void *priv, const char * style_name)
617
{
618
    MovTextContext *s = priv;
619
    ASSStyle * style;
620
621
    if (!style_name || !*style_name)
622
        style = s->ass_dialog_style;
623
    else
624
        style= ff_ass_style_get(s->ass_ctx, style_name);
625
626
    mov_text_ass_style_set(s, style);
627
}
628
629
5
static uint16_t utf8_strlen(const char *text, int len)
630
{
631
5
    uint16_t i = 0, ret = 0;
632
48
    while (i < len) {
633
43
        char c = text[i];
634
43
        if ((c & 0x80) == 0)
635
43
            i += 1;
636
        else if ((c & 0xE0) == 0xC0)
637
            i += 2;
638
        else if ((c & 0xF0) == 0xE0)
639
            i += 3;
640
        else if ((c & 0xF8) == 0xF0)
641
            i += 4;
642
        else
643
            return 0;
644
43
        ret++;
645
    }
646
5
    return ret;
647
}
648
649
5
static void mov_text_text_cb(void *priv, const char *text, int len)
650
{
651
5
    uint16_t utf8_len = utf8_strlen(text, len);
652
5
    MovTextContext *s = priv;
653
5
    av_bprint_append_data(&s->buffer, text, len);
654
    // If it's not utf-8, just use the byte length
655
5
    s->text_pos += utf8_len ? utf8_len : len;
656
5
    s->byte_count += len;
657
5
}
658
659
2
static void mov_text_new_line_cb(void *priv, int forced)
660
{
661
2
    MovTextContext *s = priv;
662
2
    av_bprint_append_data(&s->buffer, "\n", 1);
663
2
    s->text_pos += 1;
664
2
    s->byte_count += 1;
665
2
}
666
667
static const ASSCodesCallbacks mov_text_callbacks = {
668
    .text             = mov_text_text_cb,
669
    .new_line         = mov_text_new_line_cb,
670
    .style            = mov_text_style_cb,
671
    .color            = mov_text_color_cb,
672
    .alpha            = mov_text_alpha_cb,
673
    .font_name        = mov_text_font_name_cb,
674
    .font_size        = mov_text_font_size_cb,
675
    .cancel_overrides = mov_text_cancel_overrides_cb,
676
    .end              = mov_text_end_cb,
677
};
678
679
3
static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
680
                                 int bufsize, const AVSubtitle *sub)
681
{
682
3
    MovTextContext *s = avctx->priv_data;
683
    ASSDialog *dialog;
684
    int i, length;
685
    size_t j;
686
687
3
    s->byte_count = 0;
688
3
    s->text_pos = 0;
689
3
    s->count = 0;
690
3
    s->box_flags = 0;
691
6
    for (i = 0; i < sub->num_rects; i++) {
692
3
        const char *ass = sub->rects[i]->ass;
693
694
3
        if (sub->rects[i]->type != SUBTITLE_ASS) {
695
            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
696
            return AVERROR(EINVAL);
697
        }
698
699
#if FF_API_ASS_TIMING
700
3
        if (!strncmp(ass, "Dialogue: ", 10)) {
701
            int num;
702
            dialog = ff_ass_split_dialog(s->ass_ctx, ass, 0, &num);
703
            for (; dialog && num--; dialog++) {
704
                mov_text_dialog(s, dialog);
705
                ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
706
            }
707
        } else {
708
#endif
709
3
            dialog = ff_ass_split_dialog2(s->ass_ctx, ass);
710
3
            if (!dialog)
711
                return AVERROR(ENOMEM);
712
3
            mov_text_dialog(s, dialog);
713
3
            ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
714
3
            ff_ass_free_dialog(&dialog);
715
#if FF_API_ASS_TIMING
716
        }
717
#endif
718
719
12
        for (j = 0; j < box_count; j++) {
720
9
            box_types[j].encode(s, box_types[j].type);
721
        }
722
    }
723
724
3
    AV_WB16(buf, s->byte_count);
725
3
    buf += 2;
726
727
3
    if (!av_bprint_is_complete(&s->buffer)) {
728
        length = AVERROR(ENOMEM);
729
        goto exit;
730
    }
731
732
3
    if (!s->buffer.len) {
733
        length = 0;
734
        goto exit;
735
    }
736
737
3
    if (s->buffer.len > bufsize - 3) {
738
        av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
739
        length = AVERROR_BUFFER_TOO_SMALL;
740
        goto exit;
741
    }
742
743
3
    memcpy(buf, s->buffer.str, s->buffer.len);
744
3
    length = s->buffer.len + 2;
745
746
3
exit:
747
3
    av_bprint_clear(&s->buffer);
748
3
    return length;
749
}
750
751
#define OFFSET(x) offsetof(MovTextContext, x)
752
#define FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_SUBTITLE_PARAM
753
static const AVOption options[] = {
754
    { "height", "Frame height, usually video height", OFFSET(frame_height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
755
    { NULL },
756
};
757
758
static const AVClass mov_text_encoder_class = {
759
    .class_name = "MOV text enoder",
760
    .item_name  = av_default_item_name,
761
    .option     = options,
762
    .version    = LIBAVUTIL_VERSION_INT,
763
};
764
765
AVCodec ff_movtext_encoder = {
766
    .name           = "mov_text",
767
    .long_name      = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"),
768
    .type           = AVMEDIA_TYPE_SUBTITLE,
769
    .id             = AV_CODEC_ID_MOV_TEXT,
770
    .priv_data_size = sizeof(MovTextContext),
771
    .priv_class     = &mov_text_encoder_class,
772
    .init           = mov_text_encode_init,
773
    .encode_sub     = mov_text_encode_frame,
774
    .close          = mov_text_encode_close,
775
};