Coverage Report

Created: 2026-07-12 08:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/demux/mpeg/ts_hotfixes.c
Line
Count
Source
1
/*****************************************************************************
2
 * ts_hotfixes.c : MPEG PMT/PAT less streams fixups
3
 *****************************************************************************
4
 * Copyright (C) 2014-2016 - VideoLAN Authors
5
 *
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published by
8
 * the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program 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
14
 * GNU Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 *****************************************************************************/
19
#ifdef HAVE_CONFIG_H
20
# include "config.h"
21
#endif
22
23
#include <vlc_common.h>
24
#include <vlc_demux.h>
25
#include <vlc_es.h>
26
27
#ifndef _DVBPSI_DVBPSI_H_
28
 #include <dvbpsi/dvbpsi.h>
29
#endif
30
#include <dvbpsi/descriptor.h>
31
#include <dvbpsi/pat.h>
32
#include <dvbpsi/pmt.h>
33
34
#include "../../mux/mpeg/streams.h"
35
#include "../../mux/mpeg/tsutil.h"
36
#include "../../mux/mpeg/tables.h"
37
38
#include "timestamps.h"
39
#include "pes.h"
40
41
#include "ts_streams.h"
42
#include "ts_psi.h"
43
#include "ts_pid.h"
44
#include "ts_streams_private.h"
45
#include "ts.h"
46
#include "ts_hotfixes.h"
47
#include "ts_packet.h"
48
49
#include <assert.h>
50
51
void ProbePES( demux_t *p_demux, ts_pid_t *pid, const block_t *p_pkt )
52
51.1k
{
53
51.1k
    demux_sys_t *p_sys = p_demux->p_sys;
54
55
51.1k
    unsigned i_skip = PKTHeaderAndAFSize( p_pkt );
56
51.1k
    if ( p_pkt->i_buffer < i_skip )
57
0
        return;
58
51.1k
    ts_90khz_t pktpcr = GetPCR( p_pkt );
59
60
51.1k
    if( pktpcr != TS_90KHZ_INVALID )
61
6.91k
        pid->probed.i_pcr_count++;
62
63
51.1k
    size_t i_data = p_pkt->i_buffer - i_skip;
64
51.1k
    const uint8_t *p_pes = &p_pkt->p_buffer[i_skip];
65
66
51.1k
    ts_pes_header_t pesh;
67
51.1k
    ts_pes_header_init( &pesh );
68
51.1k
    if( ParsePESHeader( NULL, p_pes, i_data, &pesh ) != VLC_SUCCESS )
69
14.4k
        return;
70
71
36.6k
    if( pesh.i_dts != TS_90KHZ_INVALID )
72
5.03k
        pid->probed.i_dts_count++;
73
74
36.6k
    if( pid->probed.i_fourcc != 0 )
75
6.68k
        goto codecprobingend;
76
77
29.9k
    if( i_data < pesh.i_size + 4 )
78
1.95k
        return;
79
80
28.0k
    const uint8_t *p_data = &p_pes[pesh.i_size];
81
28.0k
    const uint8_t i_stream_id = pid->probed.i_stream_id = p_pes[3];
82
    /* NON MPEG audio & subpictures STREAM */
83
28.0k
    if(i_stream_id == 0xBD)
84
399
    {
85
399
        if( !memcmp( p_data, "\x7F\xFE\x80\x01", 4 ) )
86
10
        {
87
10
            pid->probed.i_fourcc = VLC_CODEC_DTS;
88
10
            pid->probed.i_cat = AUDIO_ES;
89
10
        }
90
389
        else if( !memcmp( p_data, "\x0B\x77", 2 ) )
91
257
        {
92
257
            pid->probed.i_fourcc = VLC_CODEC_EAC3;
93
257
            pid->probed.i_cat = AUDIO_ES;
94
257
        }
95
399
    }
96
    /* MPEG AUDIO STREAM */
97
27.6k
    else if(i_stream_id >= 0xC0 && i_stream_id <= 0xDF)
98
1.97k
    {
99
1.97k
        pid->probed.i_cat = AUDIO_ES;
100
1.97k
        if( p_data[0] == 0xFF && (p_data[1] & 0xE0) == 0xE0 &&
101
604
           (p_data[1] & 0x18) != 0x08 && (p_data[1] & 0x06) != 0x00 )
102
366
        {
103
366
            pid->probed.i_fourcc = VLC_CODEC_MPGA;
104
366
        }
105
1.60k
        else if( p_data[0] == 0xFF && (p_data[1] & 0xF6) == 0xF0 )
106
144
        {
107
144
            pid->probed.i_fourcc = VLC_CODEC_MP4A; /* ADTS */
108
144
            pid->probed.i_original_fourcc = VLC_FOURCC('A','D','T','S');
109
144
        }
110
1.97k
    }
111
    /* VIDEO STREAM */
112
25.6k
    else if( i_stream_id >= 0xE0 && i_stream_id <= 0xEF )
113
631
    {
114
631
        pid->probed.i_cat = VIDEO_ES;
115
631
        if( !memcmp( p_data, "\x00\x00\x00\x01", 4 ) )
116
32
        {
117
32
            pid->probed.i_fourcc = VLC_CODEC_H264;
118
32
        }
119
599
        else if( !memcmp( p_data, "\x00\x00\x01", 3 ) )
120
21
        {
121
21
            pid->probed.i_fourcc = VLC_CODEC_MPGV;
122
21
        }
123
631
    }
124
125
34.7k
codecprobingend:
126
    /* Track timestamps and flag missing PAT */
127
34.7k
    if( !p_sys->patfix.i_timesourcepid && pesh.i_dts != TS_90KHZ_INVALID )
128
715
    {
129
715
        p_sys->patfix.i_first_dts = FROM_SCALE(pesh.i_dts);
130
715
        p_sys->patfix.i_timesourcepid = pid->i_pid;
131
715
    }
132
34.0k
    else if( p_sys->patfix.i_timesourcepid == pid->i_pid && pesh.i_dts != TS_90KHZ_INVALID &&
133
4.06k
             p_sys->patfix.status == PAT_WAITING )
134
2.46k
    {
135
2.46k
        if( FROM_SCALE(pesh.i_dts) - p_sys->patfix.i_first_dts > MIN_PAT_INTERVAL )
136
442
            p_sys->patfix.status = PAT_MISSING;
137
2.46k
    }
138
139
34.7k
}
140
141
static void BuildPATCallback( void *p_opaque, block_t *p_block )
142
375
{
143
375
    ts_pid_t *pat_pid = (ts_pid_t *) p_opaque;
144
375
    ts_psi_Packet_Push( pat_pid, p_block->p_buffer );
145
375
    block_Release( p_block );
146
375
}
147
148
static void BuildPMTCallback( void *p_opaque, block_t *p_block )
149
374
{
150
374
    ts_pid_t *program_pid = (ts_pid_t *) p_opaque;
151
374
    assert(program_pid->type == TYPE_PMT);
152
748
    while( p_block )
153
374
    {
154
374
        ts_psi_Packet_Push( program_pid, p_block->p_buffer );
155
374
        block_t *p_next = p_block->p_next;
156
374
        block_Release( p_block );
157
374
        p_block = p_next;
158
374
    }
159
374
}
160
161
void MissingPATPMTFixup( demux_t *p_demux )
162
400
{
163
400
    demux_sys_t *p_sys = p_demux->p_sys;
164
400
    int i_program_number = 1234;
165
400
    int i_program_pid = 1337;
166
400
    int i_pcr_pid = 0x1FFF;
167
400
    int i_num_pes = 0;
168
169
400
    ts_pid_t *p_program_pid = GetPID( p_sys, i_program_pid );
170
400
    if( SEEN(p_program_pid) )
171
1
    {
172
        /* Find a free one */
173
1
        for( i_program_pid = MIN_ES_PID;
174
2
             i_program_pid <= MAX_ES_PID && SEEN(p_program_pid);
175
1
             i_program_pid++ )
176
1
        {
177
1
            p_program_pid = GetPID( p_sys, i_program_pid );
178
1
        }
179
1
    }
180
181
400
    const ts_pid_t * candidates[4] = { NULL };
182
400
    const ts_pid_t *p_pid = NULL;
183
400
    ts_pid_next_context_t pidnextctx = ts_pid_NextContextInitValue;
184
3.40k
    while( (p_pid = ts_pid_Next( &p_sys->pids, &pidnextctx )) )
185
3.00k
    {
186
3.00k
        if( !SEEN(p_pid) || p_pid->probed.i_fourcc == 0 )
187
2.44k
            continue;
188
189
557
        if( p_pid->probed.i_pcr_count && candidates[0] == NULL && false )
190
0
            candidates[0] = p_pid;
191
192
557
        if( p_pid->probed.i_cat == AUDIO_ES &&
193
541
            (candidates[1] == NULL ||
194
168
             candidates[1]->probed.i_dts_count > p_pid->probed.i_dts_count) )
195
495
            candidates[1] = p_pid;
196
197
557
        if( candidates[2] == NULL && p_pid != candidates[1] &&
198
40
            p_pid->probed.i_dts_count > 0 )
199
16
            candidates[2] = p_pid;
200
201
557
        if( candidates[3] == NULL )
202
375
            candidates[3] = p_pid;
203
204
557
        i_num_pes++;
205
557
    }
206
207
878
    for(int i=0; i<4; i++)
208
853
    {
209
853
        if(!candidates[i])
210
478
            continue;
211
375
        i_pcr_pid = candidates[i]->i_pid;
212
375
        p_sys->patfix.b_pcrhasnopcrfield = (candidates[i]->probed.i_pcr_count < 1);
213
375
        break;
214
853
    }
215
216
400
    if( i_num_pes == 0 )
217
25
        return;
218
219
375
    tsmux_stream_t patstream =
220
375
    {
221
375
        .i_pid = 0,
222
375
        .i_continuity_counter = 0x10,
223
375
        .b_discontinuity = false
224
375
    };
225
226
375
    tsmux_stream_t pmtprogramstream =
227
375
    {
228
375
        .i_pid = i_program_pid,
229
375
        .i_continuity_counter = 0x0,
230
375
        .b_discontinuity = false
231
375
    };
232
233
375
    dvbpsi_t *handle = dvbpsi_new( NULL, DVBPSI_MSG_DEBUG );
234
375
    if( !handle )
235
0
        return;
236
237
375
    BuildPAT( handle,
238
375
            &p_sys->pids.pat, BuildPATCallback,
239
375
            0, 1,
240
375
            &patstream,
241
375
            1, &pmtprogramstream, &i_program_number );
242
243
    /* PAT callback should have been triggered */
244
375
    if( p_program_pid->type != TYPE_PMT )
245
1
    {
246
1
        dvbpsi_delete( handle );
247
1
        msg_Err( p_demux, "PAT creation failed" );
248
1
        return;
249
1
    }
250
251
374
    ts_mux_standard mux_standard = (p_sys->standard == TS_STANDARD_ATSC) ? TS_MUX_STANDARD_ATSC
252
374
                                                                         : TS_MUX_STANDARD_DVB;
253
374
    struct esstreams_t
254
374
    {
255
374
        pesmux_stream_t pes;
256
374
        tsmux_stream_t ts;
257
374
        es_format_t fmt;
258
374
    };
259
260
374
    struct esstreams_t *esstreams = calloc( i_num_pes, sizeof(struct esstreams_t) );
261
374
    pes_mapped_stream_t *mapped = calloc( i_num_pes, sizeof(pes_mapped_stream_t) );
262
374
    if( esstreams && mapped )
263
374
    {
264
374
        int j=0;
265
3.19k
        for( int i=0; i<p_sys->pids.i_all; i++ )
266
2.81k
        {
267
2.81k
            p_pid = p_sys->pids.pp_all[i];
268
269
2.81k
            if( !SEEN(p_pid) ||
270
2.44k
                p_pid->probed.i_fourcc == 0 )
271
2.26k
                continue;
272
273
556
            es_format_Init(&esstreams[j].fmt, p_pid->probed.i_cat, p_pid->probed.i_fourcc);
274
556
            esstreams[j].fmt.i_original_fourcc = p_pid->probed.i_original_fourcc;
275
276
556
            if( VLC_SUCCESS !=
277
556
                FillPMTESParams(mux_standard, &esstreams[j].fmt, &esstreams[j].ts, &esstreams[j].pes ) )
278
0
            {
279
0
                es_format_Clean( &esstreams[j].fmt );
280
0
                continue;
281
0
            }
282
283
            /* Important for correct remapping: Enforce probed PES stream id */
284
556
            esstreams[j].pes.i_stream_id = p_pid->probed.i_stream_id;
285
286
556
            esstreams[j].ts.i_pid = p_pid->i_pid;
287
556
            mapped[j].pes = &esstreams[j].pes;
288
556
            mapped[j].ts = &esstreams[j].ts;
289
556
            mapped[j].fmt = &esstreams[j].fmt;
290
556
            j++;
291
556
        }
292
293
374
        BuildPMT( handle, VLC_OBJECT(p_demux),
294
374
                 mux_standard,
295
374
                p_program_pid, BuildPMTCallback,
296
374
                0, 1,
297
374
                i_pcr_pid,
298
374
                NULL,
299
374
                1, &pmtprogramstream, &i_program_number,
300
374
                j, mapped );
301
302
        /* Cleanup */
303
930
        for( int i=0; i<j; i++ )
304
556
            es_format_Clean( &esstreams[i].fmt );
305
374
    }
306
374
    free(esstreams);
307
374
    free(mapped);
308
309
374
    dvbpsi_delete( handle );
310
374
}