Coverage Report

Created: 2026-06-09 09:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/demux/stl.c
Line
Count
Source
1
/*****************************************************************************
2
 * stl.c: EBU STL demuxer
3
 *****************************************************************************
4
 * Copyright (C) 2010 Laurent Aimar
5
 *
6
 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program; if not, write to the Free Software Foundation,
20
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21
 *****************************************************************************/
22
23
/*****************************************************************************
24
 * Preamble
25
 *****************************************************************************/
26
#ifdef HAVE_CONFIG_H
27
# include "config.h"
28
#endif
29
#include <assert.h>
30
31
#include <vlc_common.h>
32
#include <vlc_plugin.h>
33
#include <vlc_demux.h>
34
35
/*****************************************************************************
36
 * Module descriptor
37
 *****************************************************************************/
38
static int  Open (vlc_object_t *);
39
static void Close(vlc_object_t *);
40
41
168
vlc_module_begin()
42
84
    set_description(N_("EBU STL subtitles parser"))
43
84
    set_subcategory(SUBCAT_INPUT_DEMUX)
44
84
    set_capability("demux", 4)
45
168
    set_callbacks(Open, Close)
46
84
    add_shortcut("stl", "subtitle")
47
84
vlc_module_end()
48
49
/*****************************************************************************
50
 * Local definitions/prototypes
51
 *****************************************************************************/
52
typedef struct {
53
    vlc_tick_t start;
54
    vlc_tick_t stop;
55
    size_t  blocknumber;
56
    size_t  count;
57
} stl_entry_t;
58
59
typedef struct
60
{
61
    size_t      count;
62
    stl_entry_t *index;
63
64
    es_out_id_t *es;
65
66
    size_t      current;
67
    vlc_tick_t  next_date;
68
    bool        b_slave;
69
    bool        b_first_time;
70
} demux_sys_t;
71
72
static size_t ParseInteger(uint8_t *data, size_t size)
73
8.11k
{
74
8.11k
    char tmp[16];
75
8.11k
    assert(size < sizeof(tmp));
76
8.11k
    memcpy(tmp, data, size);
77
8.11k
    tmp[size] = '\0';
78
79
8.11k
    return strtol(tmp, NULL, 10);
80
8.11k
}
81
static vlc_tick_t ParseTimeCode(uint8_t *data, double fps)
82
214k
{
83
214k
    return CLOCK_FREQ * (data[0] * 3600 +
84
214k
                         data[1] *   60 +
85
214k
                         data[2] *    1 +
86
214k
                         data[3] /  fps);
87
214k
}
88
static vlc_tick_t ParseTextTimeCode(uint8_t *data, double fps)
89
1.35k
{
90
1.35k
    uint8_t tmp[4];
91
6.76k
    for (int i = 0; i < 4; i++)
92
5.41k
        tmp[i] = ParseInteger(&data[2 * i], 2);
93
1.35k
    return ParseTimeCode(tmp, fps);
94
1.35k
}
95
96
static int Control(demux_t *demux, int query, va_list args)
97
0
{
98
0
    demux_sys_t *sys = demux->p_sys;
99
0
    switch(query) {
100
0
    case DEMUX_CAN_SEEK:
101
0
        return vlc_stream_vaControl(demux->s, query, args);
102
0
    case DEMUX_GET_LENGTH: {
103
0
        *va_arg(args, vlc_tick_t *) =
104
0
            sys->count > 0 ? sys->index[sys->count-1].stop : 0;
105
0
        return VLC_SUCCESS;
106
0
    }
107
0
    case DEMUX_GET_TIME: {
108
0
        vlc_tick_t *t = va_arg(args, vlc_tick_t *);
109
0
        *t = sys->next_date - var_GetInteger(vlc_object_parent(demux),
110
0
                                             "spu-delay");
111
0
        if( *t < 0 )
112
0
            *t = sys->next_date;
113
0
        return VLC_SUCCESS;
114
0
    }
115
0
    case DEMUX_SET_NEXT_DEMUX_TIME: {
116
0
        sys->b_slave = true;
117
0
        sys->next_date = va_arg(args, vlc_tick_t);
118
0
        return VLC_SUCCESS;
119
0
    }
120
0
    case DEMUX_SET_TIME: {
121
0
        vlc_tick_t t = va_arg(args, vlc_tick_t);
122
0
        for( size_t i = 0; i + 1< sys->count; i++ )
123
0
        {
124
0
            if( sys->index[i + 1].start >= t &&
125
0
                vlc_stream_Seek(demux->s, 1024 + 128LL * sys->index[i].blocknumber) == VLC_SUCCESS )
126
0
            {
127
0
                sys->current = i;
128
0
                sys->next_date = t;
129
0
                sys->b_first_time = true;
130
0
                return VLC_SUCCESS;
131
0
            }
132
0
        }
133
0
        break;
134
0
    }
135
0
    case DEMUX_SET_POSITION:
136
0
    {
137
0
        double f = va_arg( args, double );
138
0
        if(sys->count && sys->index[sys->count-1].stop > 0)
139
0
        {
140
0
            vlc_tick_t i64 = f * sys->index[sys->count-1].stop;
141
0
            return demux_Control(demux, DEMUX_SET_TIME, i64);
142
0
        }
143
0
        break;
144
0
    }
145
0
    case DEMUX_GET_POSITION:
146
0
    {
147
0
        double *pf = va_arg(args, double *);
148
0
        if(sys->current >= sys->count)
149
0
        {
150
0
            *pf = 1.0;
151
0
        }
152
0
        else if(sys->count > 0 && sys->index[sys->count-1].stop > 0)
153
0
        {
154
0
            *pf = sys->next_date - var_GetInteger(vlc_object_parent(demux),
155
0
                                                  "spu-delay");
156
0
            if(*pf < 0)
157
0
               *pf = sys->next_date;
158
0
            *pf /= sys->index[sys->count-1].stop;
159
0
        }
160
0
        else
161
0
        {
162
0
            *pf = 0.0;
163
0
        }
164
0
        return VLC_SUCCESS;
165
0
    }
166
0
    case DEMUX_CAN_PAUSE:
167
0
    case DEMUX_SET_PAUSE_STATE:
168
0
    case DEMUX_CAN_CONTROL_PACE:
169
0
    case DEMUX_GET_PTS_DELAY: {
170
0
        return demux_vaControlHelper( demux->s, 0, -1, 0, 1, query, args );
171
0
    }
172
0
    default:
173
0
        break;
174
0
    }
175
0
    return VLC_EGENERIC;
176
0
}
177
178
static int Demux(demux_t *demux)
179
2.83G
{
180
2.83G
    demux_sys_t *sys = demux->p_sys;
181
182
2.83G
    vlc_tick_t i_barrier = sys->next_date
183
2.83G
        - var_GetInteger(vlc_object_parent(demux), "spu-delay");
184
2.83G
    if(i_barrier < 0)
185
0
        i_barrier = sys->next_date;
186
187
2.83G
    while(sys->current < sys->count &&
188
2.83G
          sys->index[sys->current].start <= i_barrier)
189
38.2k
    {
190
38.2k
        stl_entry_t *s = &sys->index[sys->current];
191
192
38.2k
        if (!sys->b_slave && sys->b_first_time)
193
1.13k
        {
194
1.13k
            es_out_SetPCR(demux->out, VLC_TICK_0 + i_barrier);
195
1.13k
            sys->b_first_time = false;
196
1.13k
        }
197
198
        /* Might be a gap in block # */
199
38.2k
        const uint64_t i_pos = 1024 + 128LL * s->blocknumber;
200
38.2k
        if(i_pos != vlc_stream_Tell(demux->s) &&
201
26.7k
           vlc_stream_Seek( demux->s, i_pos ) != VLC_SUCCESS )
202
0
            return VLC_DEMUXER_EOF;
203
204
38.2k
        block_t *b = vlc_stream_Block(demux->s, 128);
205
38.2k
        if (b && b->i_buffer == 128)
206
38.2k
        {
207
38.2k
            b->i_dts =
208
38.2k
            b->i_pts = VLC_TICK_0 + s->start;
209
38.2k
            if (s->stop > s->start)
210
12.3k
                b->i_length = s->stop - s->start;
211
38.2k
            es_out_Send(demux->out, sys->es, b);
212
38.2k
        }
213
0
        else
214
0
        {
215
0
            if(b)
216
0
                block_Release(b);
217
0
            return VLC_DEMUXER_EOF;
218
0
        }
219
38.2k
        sys->current++;
220
38.2k
    }
221
222
2.83G
    if (!sys->b_slave)
223
2.83G
    {
224
2.83G
        es_out_SetPCR(demux->out, VLC_TICK_0 + i_barrier);
225
2.83G
        sys->next_date += VLC_TICK_FROM_MS(125);
226
2.83G
    }
227
228
2.83G
    return sys->current < sys->count ? VLC_DEMUXER_SUCCESS : VLC_DEMUXER_EOF;
229
2.83G
}
230
231
static int Open(vlc_object_t *object)
232
4.82k
{
233
4.82k
    demux_t *demux = (demux_t*)object;
234
235
4.82k
    const uint8_t *peek;
236
4.82k
    if (vlc_stream_Peek(demux->s, &peek, 11) != 11)
237
8
        return VLC_EGENERIC;
238
239
4.82k
    bool is_stl_25 = !memcmp(&peek[3], "STL25.01", 8);
240
4.81k
    bool is_stl_30 = !memcmp(&peek[3], "STL30.01", 8);
241
4.81k
    if (!is_stl_25 && !is_stl_30)
242
3.40k
        return VLC_EGENERIC;
243
1.40k
    const double fps = is_stl_25 ? 25 : 30;
244
245
1.40k
    uint8_t header[1024];
246
1.40k
    if (vlc_stream_Read(demux->s, header, sizeof(header)) != sizeof(header)) {
247
53
        msg_Err(demux, "Incomplete EBU STL header");
248
53
        return VLC_EGENERIC;
249
53
    }
250
1.35k
    const int cct = ParseInteger(&header[12], 2);
251
1.35k
    const vlc_tick_t program_start = ParseTextTimeCode(&header[256], fps);
252
1.35k
    const size_t tti_count = ParseInteger(&header[238], 5);
253
1.35k
    if (!tti_count)
254
10
        return VLC_EGENERIC;
255
1.34k
    msg_Dbg(demux, "Detected EBU STL : CCT=%d TTI=%zu start=%8.8s %"PRId64, cct, tti_count, &header[256], program_start);
256
257
1.34k
    demux_sys_t *sys = malloc(sizeof(*sys));
258
1.34k
    if(!sys)
259
0
        return VLC_EGENERIC;
260
261
1.34k
    sys->b_slave   = false;
262
1.34k
    sys->b_first_time = true;
263
1.34k
    sys->next_date = 0;
264
1.34k
    sys->current   = 0;
265
1.34k
    sys->count     = 0;
266
1.34k
    sys->index     = calloc(tti_count, sizeof(*sys->index));
267
1.34k
    if(!sys->index)
268
28
    {
269
28
        free(sys);
270
28
        return VLC_EGENERIC;
271
28
    }
272
273
1.34k
    bool comment = false;
274
1.31k
    stl_entry_t *s = &sys->index[0];
275
1.31k
    s->count = 0;
276
277
155k
    for (size_t i = 0; i < tti_count; i++) {
278
155k
        uint8_t tti[16];
279
155k
        if (vlc_stream_Read(demux->s, tti, 16) != 16 ||
280
154k
            vlc_stream_Read(demux->s, NULL, 112) != 112) {
281
1.20k
            msg_Warn(demux, "Incomplete EBU STL file");
282
1.20k
            break;
283
1.20k
        }
284
153k
        const int ebn = tti[3];
285
153k
        if (ebn >= 0xf0 && ebn <= 0xfd)
286
1.06k
            continue;
287
152k
        if (ebn == 0xfe)
288
355
            continue;
289
290
152k
        if (s->count <= 0) {
291
68.9k
            comment  = tti[15] != 0;
292
68.9k
            s->start = ParseTimeCode(&tti[5], fps) - program_start;
293
68.9k
            s->stop  = ParseTimeCode(&tti[9], fps) - program_start;
294
68.9k
            s->blocknumber = i;
295
68.9k
        }
296
152k
        s->count++;
297
152k
        if (ebn == 0xff && !comment)
298
38.2k
            s = &sys->index[++sys->count];
299
152k
        if (ebn == 0xff && sys->count < tti_count)
300
68.5k
            s->count = 0;
301
152k
    }
302
303
1.31k
    demux->p_sys = sys;
304
1.31k
    if (sys->count == 0 ||
305
1.13k
        vlc_stream_Seek(demux->s, 1024 + 128LL * sys->index[0].blocknumber) != VLC_SUCCESS)
306
178
    {
307
178
        Close(object);
308
178
        return VLC_EGENERIC;
309
178
    }
310
311
1.13k
    es_format_t fmt;
312
1.13k
    es_format_Init(&fmt, SPU_ES, VLC_CODEC_EBU_STL);
313
1.13k
    fmt.i_extra = sizeof(header);
314
1.13k
    fmt.p_extra = header;
315
316
1.13k
    fmt.i_id = 0;
317
1.13k
    sys->es = es_out_Add(demux->out, &fmt);
318
1.13k
    fmt.i_extra = 0;
319
1.13k
    fmt.p_extra = NULL;
320
1.13k
    es_format_Clean(&fmt);
321
322
1.13k
    if(sys->es == NULL)
323
0
    {
324
0
        Close(object);
325
0
        return VLC_EGENERIC;
326
0
    }
327
328
1.13k
    demux->p_sys      = sys;
329
1.13k
    demux->pf_demux   = Demux;
330
1.13k
    demux->pf_control = Control;
331
1.13k
    return VLC_SUCCESS;
332
1.13k
}
333
334
static void Close(vlc_object_t *object)
335
2.35k
{
336
2.35k
    demux_t *demux = (demux_t*)object;
337
2.35k
    demux_sys_t *sys = demux->p_sys;
338
339
2.35k
    free(sys->index);
340
2.35k
    free(sys);
341
2.35k
}
342