GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavcodec/ttmlenc.c Lines: 46 77 59.7 %
Date: 2021-04-20 04:37:23 Branches: 13 40 32.5 %

Line Branch Exec Source
1
/*
2
 * TTML subtitle encoder
3
 * Copyright (c) 2020 24i
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
/**
23
 * @file
24
 * TTML subtitle encoder
25
 * @see https://www.w3.org/TR/ttml1/
26
 * @see https://www.w3.org/TR/ttml2/
27
 * @see https://www.w3.org/TR/ttml-imsc/rec
28
 */
29
30
#include "avcodec.h"
31
#include "internal.h"
32
#include "libavutil/avstring.h"
33
#include "libavutil/bprint.h"
34
#include "libavutil/internal.h"
35
#include "ass_split.h"
36
#include "ass.h"
37
#include "ttmlenc.h"
38
39
typedef struct {
40
    AVCodecContext *avctx;
41
    ASSSplitContext *ass_ctx;
42
    AVBPrint buffer;
43
} TTMLContext;
44
45
102
static void ttml_text_cb(void *priv, const char *text, int len)
46
{
47
102
    TTMLContext *s = priv;
48
102
    AVBPrint cur_line = { 0 };
49
102
    AVBPrint *buffer = &s->buffer;
50
51
102
    av_bprint_init(&cur_line, len, AV_BPRINT_SIZE_UNLIMITED);
52
53
102
    av_bprint_append_data(&cur_line, text, len);
54
102
    if (!av_bprint_is_complete(&cur_line)) {
55
        av_log(s->avctx, AV_LOG_ERROR,
56
               "Failed to move the current subtitle dialog to AVBPrint!\n");
57
        av_bprint_finalize(&cur_line, NULL);
58
        return;
59
    }
60
61
62
102
    av_bprint_escape(buffer, cur_line.str, NULL, AV_ESCAPE_MODE_XML,
63
                     0);
64
65
102
    av_bprint_finalize(&cur_line, NULL);
66
}
67
68
65
static void ttml_new_line_cb(void *priv, int forced)
69
{
70
65
    TTMLContext *s = priv;
71
72
65
    av_bprintf(&s->buffer, "<br/>");
73
65
}
74
75
static const ASSCodesCallbacks ttml_callbacks = {
76
    .text             = ttml_text_cb,
77
    .new_line         = ttml_new_line_cb,
78
};
79
80
37
static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
81
                             int bufsize, const AVSubtitle *sub)
82
{
83
37
    TTMLContext *s = avctx->priv_data;
84
    ASSDialog *dialog;
85
    int i;
86
87
37
    av_bprint_clear(&s->buffer);
88
89
74
    for (i=0; i<sub->num_rects; i++) {
90
37
        const char *ass = sub->rects[i]->ass;
91
92
37
        if (sub->rects[i]->type != SUBTITLE_ASS) {
93
            av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
94
            return AVERROR(EINVAL);
95
        }
96
97
#if FF_API_ASS_TIMING
98
37
        if (!strncmp(ass, "Dialogue: ", 10)) {
99
            int num;
100
            dialog = ff_ass_split_dialog(s->ass_ctx, ass, 0, &num);
101
102
            for (; dialog && num--; dialog++) {
103
                int ret = ff_ass_split_override_codes(&ttml_callbacks, s,
104
                                                      dialog->text);
105
                int log_level = (ret != AVERROR_INVALIDDATA ||
106
                                 avctx->err_recognition & AV_EF_EXPLODE) ?
107
                                AV_LOG_ERROR : AV_LOG_WARNING;
108
109
                if (ret < 0) {
110
                    av_log(avctx, log_level,
111
                           "Splitting received ASS dialog failed: %s\n",
112
                           av_err2str(ret));
113
114
                    if (log_level == AV_LOG_ERROR)
115
                        return ret;
116
                }
117
            }
118
        } else {
119
#endif
120
37
            dialog = ff_ass_split_dialog2(s->ass_ctx, ass);
121
37
            if (!dialog)
122
                return AVERROR(ENOMEM);
123
124
            {
125
37
                int ret = ff_ass_split_override_codes(&ttml_callbacks, s,
126
37
                                                      dialog->text);
127
37
                int log_level = (ret != AVERROR_INVALIDDATA ||
128
                                 avctx->err_recognition & AV_EF_EXPLODE) ?
129
37
                                AV_LOG_ERROR : AV_LOG_WARNING;
130
131
37
                if (ret < 0) {
132
                    av_log(avctx, log_level,
133
                           "Splitting received ASS dialog text %s failed: %s\n",
134
                           dialog->text,
135
                           av_err2str(ret));
136
137
                    if (log_level == AV_LOG_ERROR) {
138
                        ff_ass_free_dialog(&dialog);
139
                        return ret;
140
                    }
141
                }
142
143
37
                ff_ass_free_dialog(&dialog);
144
            }
145
#if FF_API_ASS_TIMING
146
        }
147
#endif
148
    }
149
150
37
    if (!av_bprint_is_complete(&s->buffer))
151
        return AVERROR(ENOMEM);
152
37
    if (!s->buffer.len)
153
        return 0;
154
155
    // force null-termination, so in case our destination buffer is
156
    // too small, the return value is larger than bufsize minus null.
157
37
    if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) {
158
        av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n");
159
        return AVERROR_BUFFER_TOO_SMALL;
160
    }
161
162
37
    return s->buffer.len;
163
}
164
165
1
static av_cold int ttml_encode_close(AVCodecContext *avctx)
166
{
167
1
    TTMLContext *s = avctx->priv_data;
168
169
1
    ff_ass_split_free(s->ass_ctx);
170
171
1
    av_bprint_finalize(&s->buffer, NULL);
172
173
1
    return 0;
174
}
175
176
1
static av_cold int ttml_encode_init(AVCodecContext *avctx)
177
{
178
1
    TTMLContext *s = avctx->priv_data;
179
180
1
    s->avctx   = avctx;
181
182
1
    if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) {
183
        return AVERROR_INVALIDDATA;
184
    }
185
186
1
    if (!(avctx->extradata = av_mallocz(TTMLENC_EXTRADATA_SIGNATURE_SIZE +
187
                                        1 + AV_INPUT_BUFFER_PADDING_SIZE))) {
188
        return AVERROR(ENOMEM);
189
    }
190
191
1
    avctx->extradata_size = TTMLENC_EXTRADATA_SIGNATURE_SIZE;
192
1
    memcpy(avctx->extradata, TTMLENC_EXTRADATA_SIGNATURE,
193
           TTMLENC_EXTRADATA_SIGNATURE_SIZE);
194
195
1
    av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
196
197
1
    return 0;
198
}
199
200
AVCodec ff_ttml_encoder = {
201
    .name           = "ttml",
202
    .long_name      = NULL_IF_CONFIG_SMALL("TTML subtitle"),
203
    .type           = AVMEDIA_TYPE_SUBTITLE,
204
    .id             = AV_CODEC_ID_TTML,
205
    .priv_data_size = sizeof(TTMLContext),
206
    .init           = ttml_encode_init,
207
    .encode_sub     = ttml_encode_frame,
208
    .close          = ttml_encode_close,
209
    .capabilities   = FF_CODEC_CAP_INIT_CLEANUP,
210
};