Coverage Report

Created: 2025-11-16 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/textdec.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2012 Clément Bœsch
3
 *
4
 * This file is part of FFmpeg.
5
 *
6
 * FFmpeg is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * FFmpeg is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with FFmpeg; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
/**
22
 * @file
23
 * Raw subtitles decoder
24
 */
25
26
#include "config_components.h"
27
28
#include "avcodec.h"
29
#include "ass.h"
30
#include "codec_internal.h"
31
#include "libavutil/attributes.h"
32
#include "libavutil/bprint.h"
33
#include "libavutil/opt.h"
34
35
typedef struct {
36
    AVClass *class;
37
    const char *linebreaks;
38
    int keep_ass_markup;
39
    int readorder;
40
} TextContext;
41
42
#define OFFSET(x) offsetof(TextContext, x)
43
#define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
44
static const AVOption options[] = {
45
    { "keep_ass_markup", "Set if ASS tags must be escaped", OFFSET(keep_ass_markup), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags=SD },
46
    { NULL }
47
};
48
49
static int text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
50
                             int *got_sub_ptr, const AVPacket *avpkt)
51
876k
{
52
876k
    int ret = 0;
53
876k
    AVBPrint buf;
54
876k
    const char *ptr = avpkt->data;
55
876k
    TextContext *text = avctx->priv_data;
56
57
876k
    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
58
876k
    if (ptr && avpkt->size > 0 && *ptr) {
59
812k
        ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
60
812k
        ret = ff_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL);
61
812k
    }
62
876k
    av_bprint_finalize(&buf, NULL);
63
876k
    if (ret < 0)
64
0
        return ret;
65
876k
    *got_sub_ptr = sub->num_rects > 0;
66
876k
    return avpkt->size;
67
876k
}
68
69
static av_cold void text_flush(AVCodecContext *avctx)
70
178k
{
71
178k
    TextContext *text = avctx->priv_data;
72
178k
    if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
73
178k
        text->readorder = 0;
74
178k
}
75
76
static const AVClass textsub_decoder_class = {
77
    .class_name = "text/vplayer/stl/pjs/subviewer1 decoder",
78
    .item_name  = av_default_item_name,
79
    .option     = options,
80
    .version    = LIBAVUTIL_VERSION_INT,
81
};
82
83
#if CONFIG_TEXT_DECODER
84
const FFCodec ff_text_decoder = {
85
    .p.name         = "text",
86
    CODEC_LONG_NAME("Raw text subtitle"),
87
    .priv_data_size = sizeof(TextContext),
88
    .p.type         = AVMEDIA_TYPE_SUBTITLE,
89
    .p.id           = AV_CODEC_ID_TEXT,
90
    FF_CODEC_DECODE_SUB_CB(text_decode_frame),
91
    .init           = ff_ass_subtitle_header_default,
92
    .p.priv_class   = &textsub_decoder_class,
93
    .flush          = text_flush,
94
};
95
#endif
96
97
#if CONFIG_VPLAYER_DECODER || CONFIG_PJS_DECODER || CONFIG_SUBVIEWER1_DECODER || CONFIG_STL_DECODER
98
99
static av_cold int linebreak_init(AVCodecContext *avctx)
100
4.17k
{
101
4.17k
    TextContext *text = avctx->priv_data;
102
4.17k
    text->linebreaks = "|";
103
4.17k
    return ff_ass_subtitle_header_default(avctx);
104
4.17k
}
105
106
#if CONFIG_VPLAYER_DECODER
107
const FFCodec ff_vplayer_decoder = {
108
    .p.name         = "vplayer",
109
    CODEC_LONG_NAME("VPlayer subtitle"),
110
    .priv_data_size = sizeof(TextContext),
111
    .p.type         = AVMEDIA_TYPE_SUBTITLE,
112
    .p.id           = AV_CODEC_ID_VPLAYER,
113
    FF_CODEC_DECODE_SUB_CB(text_decode_frame),
114
    .init           = linebreak_init,
115
    .p.priv_class   = &textsub_decoder_class,
116
    .flush          = text_flush,
117
};
118
#endif
119
120
#if CONFIG_STL_DECODER
121
const FFCodec ff_stl_decoder = {
122
    .p.name         = "stl",
123
    CODEC_LONG_NAME("Spruce subtitle format"),
124
    .priv_data_size = sizeof(TextContext),
125
    .p.type         = AVMEDIA_TYPE_SUBTITLE,
126
    .p.id           = AV_CODEC_ID_STL,
127
    FF_CODEC_DECODE_SUB_CB(text_decode_frame),
128
    .init           = linebreak_init,
129
    .p.priv_class   = &textsub_decoder_class,
130
    .flush          = text_flush,
131
};
132
#endif
133
134
#if CONFIG_PJS_DECODER
135
const FFCodec ff_pjs_decoder = {
136
    .p.name         = "pjs",
137
    CODEC_LONG_NAME("PJS subtitle"),
138
    .priv_data_size = sizeof(TextContext),
139
    .p.type         = AVMEDIA_TYPE_SUBTITLE,
140
    .p.id           = AV_CODEC_ID_PJS,
141
    FF_CODEC_DECODE_SUB_CB(text_decode_frame),
142
    .init           = linebreak_init,
143
    .p.priv_class   = &textsub_decoder_class,
144
    .flush          = text_flush,
145
};
146
#endif
147
148
#if CONFIG_SUBVIEWER1_DECODER
149
const FFCodec ff_subviewer1_decoder = {
150
    .p.name         = "subviewer1",
151
    CODEC_LONG_NAME("SubViewer1 subtitle"),
152
    .priv_data_size = sizeof(TextContext),
153
    .p.type         = AVMEDIA_TYPE_SUBTITLE,
154
    .p.id           = AV_CODEC_ID_SUBVIEWER1,
155
    FF_CODEC_DECODE_SUB_CB(text_decode_frame),
156
    .init           = linebreak_init,
157
    .p.priv_class   = &textsub_decoder_class,
158
    .flush          = text_flush,
159
};
160
#endif
161
162
#endif /* text subtitles with '|' line break */