Coverage Report

Created: 2026-05-23 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavformat/mpsubdec.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
 * MPlayer subtitles format demuxer
24
 */
25
26
#include "avformat.h"
27
#include "demux.h"
28
#include "internal.h"
29
#include "subtitles.h"
30
31
19.6M
#define TSBASE 10000000
32
33
typedef struct {
34
    FFDemuxSubtitlesQueue q;
35
} MPSubContext;
36
37
static int mpsub_probe(const AVProbeData *p)
38
958k
{
39
958k
    const char *ptr     = p->buf;
40
958k
    const char *ptr_end = p->buf + p->buf_size;
41
42
6.84M
    while (ptr < ptr_end) {
43
6.72M
        int inc;
44
45
6.72M
        if (!memcmp(ptr, "FORMAT=TIME", 11))
46
6.13k
            return AVPROBE_SCORE_EXTENSION;
47
6.71M
        if (!memcmp(ptr, "FORMAT=", 7))
48
1.06k
            return AVPROBE_SCORE_EXTENSION / 3;
49
6.71M
        inc = ff_subtitles_next_line(ptr);
50
6.71M
        if (!inc)
51
831k
            break;
52
5.88M
        ptr += inc;
53
5.88M
    }
54
951k
    return 0;
55
958k
}
56
57
static int parse_line(const char *line, int64_t *value, int64_t *value2)
58
2.90M
{
59
2.90M
    int vi, p1, p2;
60
61
7.68M
    for (vi = 0; vi < 2; vi++) {
62
5.32M
        long long intval, fracval;
63
5.32M
        int n = av_sscanf(line, "%lld%n.%lld%n", &intval, &p1, &fracval, &p2);
64
5.32M
        if (n <= 0 || intval < INT64_MIN / TSBASE || intval > INT64_MAX / TSBASE)
65
541k
            return AVERROR_INVALIDDATA;
66
67
4.78M
        intval *= TSBASE;
68
69
4.78M
        if (n == 2) {
70
18.7k
            if (fracval < 0)
71
1.08k
                return AVERROR_INVALIDDATA;
72
93.5k
            for (;p2 - p1 < 7 + 1; p1--)
73
75.8k
                fracval *= 10;
74
32.0k
            for (;p2 - p1 > 7 + 1; p1++)
75
14.3k
                fracval /= 10;
76
17.7k
            if (intval > 0) intval = av_sat_add64(intval, fracval);
77
11.7k
            else            intval = av_sat_sub64(intval, fracval);
78
17.7k
            line += p2;
79
17.7k
        } else
80
4.76M
            line += p1;
81
82
4.78M
        *value = intval;
83
84
4.78M
        value = value2;
85
4.78M
    }
86
87
2.35M
    return 0;
88
2.90M
}
89
90
static int mpsub_read_header(AVFormatContext *s)
91
3.77k
{
92
3.77k
    MPSubContext *mpsub = s->priv_data;
93
3.77k
    AVStream *st;
94
3.77k
    AVBPrint buf;
95
3.77k
    AVRational pts_info = (AVRational){ TSBASE, 1 }; // ts based by default
96
3.77k
    int res = 0;
97
3.77k
    int64_t current_pts = 0;
98
3.77k
    int i;
99
3.77k
    int common_factor = 0;
100
101
3.77k
    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
102
103
2.90M
    while (!avio_feof(s->pb)) {
104
2.90M
        char line[1024];
105
2.90M
        int64_t start, duration;
106
2.90M
        int fps, len = ff_get_line(s->pb, line, sizeof(line));
107
108
2.90M
        if (!len)
109
402
            break;
110
111
2.90M
        line[strcspn(line, "\r\n")] = 0;
112
113
2.90M
        if (sscanf(line, "FORMAT=%d", &fps) == 1 && fps > 3 && fps < 100) {
114
            /* frame based timing */
115
1.99k
            pts_info = (AVRational){ TSBASE * fps, 1 };
116
2.90M
        } else if (parse_line(line, &start, &duration) >= 0) {
117
2.35M
            AVPacket *sub;
118
2.35M
            const int64_t pos = avio_tell(s->pb);
119
120
2.35M
            res = ff_subtitles_read_chunk(s->pb, &buf);
121
2.35M
            if (res < 0) goto end;
122
2.35M
            if (buf.len) {
123
2.35M
                sub = ff_subtitles_queue_insert_bprint(&mpsub->q, &buf, 0);
124
2.35M
                if (!sub) {
125
0
                    res = AVERROR(ENOMEM);
126
0
                    goto end;
127
0
                }
128
2.35M
                if (   current_pts < 0 && start < INT64_MIN - current_pts
129
2.35M
                    || current_pts > 0 && start > INT64_MAX - current_pts) {
130
44
                    res = AVERROR_INVALIDDATA;
131
44
                    goto end;
132
44
                }
133
2.35M
                sub->pts = current_pts + start;
134
2.35M
                if (duration < 0 || sub->pts > INT64_MAX - duration) {
135
195
                    res = AVERROR_INVALIDDATA;
136
195
                    goto end;
137
195
                }
138
2.35M
                sub->duration = duration;
139
140
2.35M
                common_factor = av_gcd(duration, common_factor);
141
2.35M
                common_factor = av_gcd(sub->pts, common_factor);
142
143
2.35M
                current_pts = sub->pts + duration;
144
2.35M
                sub->pos = pos;
145
2.35M
            }
146
2.35M
        }
147
2.90M
    }
148
149
3.53k
    if (common_factor > 1) {
150
1.35k
        common_factor = av_gcd(pts_info.num, common_factor);
151
2.31M
        for (i = 0; i < mpsub->q.nb_subs; i++) {
152
2.31M
            mpsub->q.subs[i]->pts      /= common_factor;
153
2.31M
            mpsub->q.subs[i]->duration /= common_factor;
154
2.31M
        }
155
1.35k
        pts_info.num /= common_factor;
156
1.35k
    }
157
158
3.53k
    st = avformat_new_stream(s, NULL);
159
3.53k
    if (!st) {
160
0
        res = AVERROR(ENOMEM);
161
0
        goto end;
162
0
    }
163
3.53k
    avpriv_set_pts_info(st, 64, pts_info.den, pts_info.num);
164
3.53k
    st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
165
3.53k
    st->codecpar->codec_id   = AV_CODEC_ID_TEXT;
166
167
3.53k
    ff_subtitles_queue_finalize(s, &mpsub->q);
168
169
3.77k
end:
170
    av_bprint_finalize(&buf, NULL);
171
3.77k
    return res;
172
3.53k
}
173
174
const FFInputFormat ff_mpsub_demuxer = {
175
    .p.name         = "mpsub",
176
    .p.long_name    = NULL_IF_CONFIG_SMALL("MPlayer subtitles"),
177
    .p.extensions   = "sub",
178
    .priv_data_size = sizeof(MPSubContext),
179
    .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP,
180
    .read_probe     = mpsub_probe,
181
    .read_header    = mpsub_read_header,
182
    .read_packet    = ff_subtitles_read_packet,
183
    .read_seek2     = ff_subtitles_read_seek,
184
    .read_close     = ff_subtitles_read_close,
185
};