Coverage Report

Created: 2026-06-09 09:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/demux/mpeg/ts_pes.c
Line
Count
Source
1
/*****************************************************************************
2
 * ts_pes.c: Transport Stream input module for VLC.
3
 *****************************************************************************
4
 * Copyright (C) 2004-2019 VLC authors and VideoLAN
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 Lesser General Public License
17
 * along with this program; if not, write to the Free Software Foundation,
18
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19
 *****************************************************************************/
20
#ifdef HAVE_CONFIG_H
21
# include "config.h"
22
#endif
23
24
#include <vlc_common.h>
25
#include <vlc_demux.h>
26
27
#include "ts_streams.h"
28
#include "ts_pid.h"
29
#include "ts_streams_private.h"
30
31
#include "ts_pes.h"
32
33
#include <assert.h>
34
35
/* Avoids largest memcpy */
36
static bool block_Split( block_t **pp_block, block_t **pp_remain, size_t i_offset )
37
0
{
38
0
    block_t *p_block = *pp_block;
39
0
    block_t *p_split = NULL;
40
0
    *pp_remain = NULL;
41
42
0
    size_t i_tocopy = p_block->i_buffer - i_offset;
43
0
    if( i_tocopy > i_offset ) /* make new block for head */
44
0
    {
45
0
        if( i_offset > 0 )
46
0
        {
47
0
            p_split = block_Alloc( i_offset );
48
0
            if( p_split == NULL )
49
0
                return false;
50
0
            memcpy( p_split->p_buffer, p_block->p_buffer, i_offset );
51
0
            p_block->p_buffer += i_offset;
52
0
            p_block->i_buffer -= i_offset;
53
0
        }
54
0
        *pp_remain = p_block;
55
0
        *pp_block = p_split;
56
0
    }
57
0
    else /* other gets the tail of our split */
58
0
    {
59
0
        if( i_tocopy > 0 )
60
0
        {
61
0
            p_split = block_Alloc( i_tocopy );
62
0
            if( p_split == NULL )
63
0
                return false;
64
0
            memcpy( p_split->p_buffer, &p_block->p_buffer[i_offset], i_tocopy );
65
0
            p_block->i_buffer -= i_tocopy;
66
0
        }
67
0
        *pp_remain = p_split;
68
0
    }
69
0
    return true;
70
0
}
71
72
static const uint8_t pes_sync[] = { 0, 0, 1 };
73
74
static bool MayHaveStartCodeOnEnd( const uint8_t *p_buf, size_t i_buf )
75
0
{
76
0
    assert(i_buf > 2);
77
0
    return !( *(--p_buf) > 1 || *(--p_buf) > 0 || *(--p_buf) > 0 );
78
0
}
79
80
static uint8_t *FindNextPESHeader( uint8_t *p_buf, size_t i_buffer )
81
0
{
82
0
    const uint8_t *p_end = &p_buf[i_buffer];
83
0
    unsigned i_bitflow = 0;
84
0
    for( ; p_buf != p_end; p_buf++ )
85
0
    {
86
0
        i_bitflow <<= 1;
87
0
        if( !*p_buf )
88
0
        {
89
0
            i_bitflow |= 1;
90
0
        }
91
0
        else if( *p_buf == 0x01 && (i_bitflow & 0x06) == 0x06 ) /* >= two zero prefixed 1 */
92
0
        {
93
0
            return p_buf - 2;
94
0
        }
95
0
    }
96
0
    return NULL;
97
0
}
98
99
static bool ts_pes_Push( ts_pes_parse_callback *cb,
100
                  ts_stream_t *p_pes, block_t *p_pkt,
101
                  bool b_unit_start, ts_90khz_t i_append_pcr )
102
3.04M
{
103
3.04M
    bool b_ret = false;
104
105
3.04M
    if ( b_unit_start && p_pes->gather.p_data )
106
895k
    {
107
895k
        block_t *p_datachain = p_pes->gather.p_data;
108
895k
        uint32_t i_flags = p_pes->gather.i_block_flags;
109
895k
        if( p_pes->gather.i_data_size &&
110
884k
            p_pes->gather.i_gathered != p_pes->gather.i_data_size )
111
354k
        {
112
            /* too early unit start resulting from packet loss */
113
            /* or ending on a pkt not belonging to PES (%15 packets loss) */
114
            /* But some encoders can't compute PES size right #28649 :/ */
115
354k
            if( p_pes->gather.i_gathered < p_pes->gather.i_data_size  ||
116
73.7k
                p_pes->gather.i_gathered > p_pes->gather.i_data_size + 16 )
117
322k
                i_flags |= BLOCK_FLAG_CORRUPTED;
118
354k
        }
119
        /* Flush the pes from pid */
120
895k
        p_pes->gather.p_data = NULL;
121
895k
        p_pes->gather.i_data_size = 0;
122
895k
        p_pes->gather.i_gathered = 0;
123
895k
        p_pes->gather.i_block_flags = 0;
124
895k
        p_pes->gather.pp_last = &p_pes->gather.p_data;
125
895k
        cb->pf_parse( cb->p_obj, cb->priv, p_datachain, i_flags, p_pes->gather.i_append_pcr );
126
895k
        b_ret = true;
127
895k
    }
128
129
3.04M
    if( b_unit_start )
130
2.41M
        p_pes->gather.i_append_pcr = i_append_pcr;
131
132
3.04M
    if( p_pkt == NULL )
133
1.51M
        return b_ret;
134
135
1.53M
    if( p_pkt->i_buffer == 0 )
136
429
    {
137
429
        block_Release( p_pkt );
138
429
        return b_ret;
139
429
    }
140
141
1.53M
    if( !b_unit_start && p_pes->gather.p_data == NULL )
142
98.1k
    {
143
        /* msg_Dbg( p_demux, "broken packet" ); */
144
98.1k
        block_Release( p_pkt );
145
98.1k
        return b_ret;
146
98.1k
    }
147
148
1.43M
    block_ChainLastAppend( &p_pes->gather.pp_last, p_pkt );
149
1.43M
    p_pes->gather.i_gathered += p_pkt->i_buffer;
150
151
1.43M
    if( p_pes->gather.i_data_size > 0 &&
152
1.41M
        p_pes->gather.i_gathered >= p_pes->gather.i_data_size )
153
603k
    {
154
        /* re-enter in Flush above */
155
603k
        assert(p_pes->gather.p_data);
156
603k
        return ts_pes_Push( cb, p_pes, NULL, true, i_append_pcr );
157
603k
    }
158
159
832k
    return b_ret;
160
1.43M
}
161
162
bool ts_pes_Drain( ts_pes_parse_callback *cb, ts_stream_t *p_pes )
163
0
{
164
0
    return ts_pes_Push( cb, p_pes, NULL, true, TS_90KHZ_INVALID );
165
0
}
166
167
bool ts_pes_Gather( ts_pes_parse_callback *cb,
168
                    ts_stream_t *p_pes, block_t *p_pkt,
169
                    bool b_unit_start, bool b_valid_scrambling,
170
                    ts_90khz_t i_append_pcr )
171
1.57M
{
172
1.57M
    bool b_ret = false;
173
1.57M
    bool b_single_payload = b_unit_start; /* Single payload in case of unit start */
174
1.57M
    bool b_aligned_ts_payload = true;
175
176
1.57M
    if( unlikely(p_pes->b_broken_PUSI_conformance) )
177
0
    {
178
        /* Stream does not conform to payload_unit_start flag
179
         * applied to PES packets (AdTech private_stream_1) */
180
0
        b_aligned_ts_payload = false;
181
0
        b_single_payload = false;
182
183
0
    }
184
185
    /* Deal with explicit packet loss */
186
1.57M
    if( p_pkt->i_flags & BLOCK_FLAG_PRIVATE_PACKET_LOSS )
187
556k
    {
188
        /* Flag unfinished unit as corrupted */
189
556k
        if ( p_pes->gather.i_gathered )
190
280k
            p_pes->gather.i_block_flags |= BLOCK_FLAG_CORRUPTED;
191
556k
        p_pkt->i_flags &= ~BLOCK_FLAG_PRIVATE_PACKET_LOSS;
192
556k
    }
193
194
    /* We'll cannot parse any pes data */
195
1.57M
    if( (p_pkt->i_flags & BLOCK_FLAG_SCRAMBLED) && b_valid_scrambling )
196
958
    {
197
958
        block_Release( p_pkt );
198
958
        return ts_pes_Push( cb, p_pes, NULL, true, i_append_pcr );
199
958
    }
200
201
    /* Seek discontinuity, we need to drop or output currently
202
     * gathered data */
203
1.57M
    if( p_pkt->i_flags & BLOCK_FLAG_PRIVATE_SOURCE_RANDOM_ACCESS )
204
2.01k
    {
205
2.01k
        p_pes->gather.i_saved = 0;
206
        /* Flush/output current */
207
2.01k
        b_ret |= ts_pes_Push( cb, p_pes, NULL, true, i_append_pcr );
208
        /* Propagate to output block to notify packetizers/decoders */
209
2.01k
        if( p_pes->p_es )
210
2.01k
            p_pes->p_es->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
211
2.01k
    }
212
    /* On dropped packets, detected by continuity counter */
213
1.57M
    else if( p_pkt->i_flags & BLOCK_FLAG_DISCONTINUITY )
214
0
    {
215
        /* If we know the final size and didn't gather enough bytes it is corrupted
216
           or if the discontinuity doesn't carry the start code */
217
0
       if( p_pes->gather.i_gathered && (p_pes->gather.i_data_size ||
218
0
                                        (b_aligned_ts_payload && !b_unit_start) ) )
219
0
           p_pes->gather.i_block_flags |= BLOCK_FLAG_CORRUPTED;
220
0
        b_ret |= ts_pes_Push( cb, p_pes, NULL, true, i_append_pcr );
221
222
        /* it can't match the target size and need to resync on sync code */
223
0
        p_pes->gather.i_data_size = 0;
224
        /* can't reuse prev bytes to lookup sync code */
225
0
        p_pes->gather.i_saved = 0;
226
        /* Propagate to output block to notify packetizers/decoders */
227
0
        if( p_pes->p_es )
228
0
            p_pes->p_es->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
229
0
        p_pes->gather.i_block_flags|= BLOCK_FLAG_DISCONTINUITY;
230
0
    }
231
    /* On dropped packets, detected by continuity counter */
232
1.57M
    else if( p_pkt->i_flags & BLOCK_FLAG_CORRUPTED )
233
0
    {
234
0
        p_pes->gather.i_block_flags |= BLOCK_FLAG_CORRUPTED;
235
        /* can't reuse prev bytes to lookup sync code */
236
0
        p_pes->gather.i_saved = 0;
237
0
    }
238
239
1.57M
    if ( unlikely(p_pes->gather.i_saved > 0) )
240
0
    {
241
        /* Saved from previous packet end */
242
0
        assert(p_pes->gather.i_saved < TS_PES_HEADER_SIZE);
243
0
        if( !b_aligned_ts_payload )
244
0
        {
245
0
            p_pkt = block_Realloc( p_pkt, p_pes->gather.i_saved, p_pkt->i_buffer );
246
0
            if( p_pkt )
247
0
                memcpy( p_pkt->p_buffer, p_pes->gather.saved, p_pes->gather.i_saved );
248
0
        }
249
0
        p_pes->gather.i_saved = 0;
250
0
    }
251
252
3.72M
    for( bool b_first_sync_done = false; p_pkt; )
253
2.19M
    {
254
2.19M
        assert( p_pes->gather.i_saved == 0 );
255
256
2.19M
        if( p_pes->gather.p_data == NULL && b_unit_start && !b_first_sync_done && p_pkt->i_buffer >= TS_PES_HEADER_SIZE )
257
657k
        {
258
657k
            if( likely(b_aligned_ts_payload) )
259
657k
            {
260
657k
                if( memcmp( p_pkt->p_buffer, pes_sync, 3 ) )
261
44.5k
                {
262
44.5k
                    block_Release( p_pkt );
263
44.5k
                    return b_ret;
264
44.5k
                }
265
657k
            }
266
0
            else
267
0
            {
268
                /* Need to find sync code */
269
0
                uint8_t *p_buf = FindNextPESHeader( p_pkt->p_buffer, p_pkt->i_buffer - 3 );
270
0
                if( p_buf == NULL )
271
0
                {
272
                    /* no first sync code */
273
0
                    if( MayHaveStartCodeOnEnd( p_pkt->p_buffer, p_pkt->i_buffer ) )
274
0
                    {
275
                        /* Drop everything except last bytes for next packet */
276
0
                        p_pkt->p_buffer += p_pkt->i_buffer - 3;
277
0
                        p_pes->gather.i_saved = p_pkt->i_buffer = 3;
278
0
                        memcpy(p_pes->gather.saved, p_pkt->p_buffer, p_pkt->i_buffer);
279
0
                    }
280
0
                    block_Release( p_pkt );
281
0
                    return b_ret;
282
0
                }
283
0
                p_pkt->i_buffer -= p_buf - p_pkt->p_buffer;
284
0
                p_pkt->p_buffer = p_buf;
285
0
            }
286
            /* now points to PES header */
287
612k
            p_pes->gather.i_data_size = GetWBE(&p_pkt->p_buffer[4]);
288
612k
            if( p_pes->gather.i_data_size > 0 )
289
607k
                p_pes->gather.i_data_size += TS_PES_HEADER_SIZE;
290
612k
            b_first_sync_done = true; /* Because if size is 0, we would not look for second sync */
291
612k
        }
292
1.53M
        else
293
1.53M
        {
294
1.53M
            assert( p_pes->gather.i_data_size > p_pes->gather.i_gathered ||
295
1.53M
                    p_pes->gather.i_data_size == 0 );
296
297
            /* If we started reading a fixed size that might not end on boundary */
298
1.53M
            if( unlikely(!b_aligned_ts_payload) &&
299
0
                p_pes->gather.i_data_size > p_pes->gather.i_gathered )
300
0
            {
301
0
                const size_t i_remain = p_pes->gather.i_data_size - p_pes->gather.i_gathered;
302
                /* Append whole block */
303
0
                if( likely(p_pkt->i_buffer <= i_remain) )
304
0
                {
305
0
                    b_ret |= ts_pes_Push( cb, p_pes, p_pkt, p_pes->gather.p_data == NULL, i_append_pcr );
306
0
                    p_pkt = NULL;
307
0
                }
308
0
                else /* p_pkt->i_buffer > i_remain */
309
0
                {
310
0
                    block_t *p_split;
311
0
                    if( !block_Split( &p_pkt, &p_split, i_remain ) )
312
0
                    {
313
0
                        block_Release( p_pkt );
314
0
                        return false;
315
0
                    }
316
0
                    p_pes->gather.i_block_flags |= BLOCK_FLAG_CORRUPTED;
317
0
                    b_ret |= ts_pes_Push( cb, p_pes, p_pkt, p_pes->gather.p_data == NULL, i_append_pcr );
318
0
                    p_pkt = p_split;
319
0
                    b_first_sync_done = false;
320
0
                }
321
0
            }
322
1.53M
            else /* if( p_pes->gather.i_data_size == 0 ) // see next packet */
323
1.53M
            {
324
1.53M
                if( likely(b_aligned_ts_payload) && b_unit_start )
325
905k
                {
326
905k
                    b_ret |= ts_pes_Push( cb, p_pes, NULL, true, i_append_pcr );
327
                    /* now points to PES header */
328
905k
                    if( p_pkt->i_buffer >= TS_PES_HEADER_SIZE )
329
902k
                    {
330
902k
                        p_pes->gather.i_data_size = GetWBE(&p_pkt->p_buffer[4]);
331
902k
                        if( p_pes->gather.i_data_size > 0 )
332
893k
                            p_pes->gather.i_data_size += TS_PES_HEADER_SIZE;
333
902k
                    }
334
905k
                }
335
                /* Append or finish current/start new PES depending on unit_start */
336
1.53M
                b_ret |= ts_pes_Push( cb, p_pes, p_pkt, b_unit_start, i_append_pcr );
337
1.53M
                p_pkt = NULL;
338
1.53M
            }
339
1.53M
        }
340
341
2.14M
        if( unlikely(p_pkt && p_pkt->i_buffer < TS_PES_HEADER_SIZE) )
342
0
        {
343
            /* save and prepend to next packet */
344
0
            assert(!b_single_payload);
345
0
            assert(p_pes->gather.i_saved == 0);
346
0
            p_pes->gather.i_saved = p_pkt->i_buffer;
347
0
            memcpy(p_pes->gather.saved, p_pkt->p_buffer, p_pkt->i_buffer);
348
0
            block_Release( p_pkt );
349
0
            p_pkt = NULL;
350
0
        }
351
2.14M
    }
352
353
1.53M
    return b_ret;
354
1.57M
}