Coverage Report

Created: 2026-05-14 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wiretap/mp2t.c
Line
Count
Source
1
/* mp2t.c
2
 *
3
 * ISO/IEC 13818-1 MPEG2-TS file format decoder for the Wiretap library.
4
 * Written by Weston Schmidt <weston_schmidt@alumni.purdue.edu>
5
 * Copyright 2012 Weston Schmidt
6
 *
7
 * Wiretap Library
8
 * SPDX-License-Identifier: GPL-2.0-or-later
9
 */
10
11
#include "config.h"
12
#include "mp2t.h"
13
14
#include <sys/types.h>
15
16
#ifdef HAVE_UNISTD_H
17
#include <unistd.h>
18
#endif
19
20
#include "wtap_module.h"
21
#include <wsutil/buffer.h>
22
#include <wsutil/pint.h>
23
#include "file_wrappers.h"
24
#include <stdlib.h>
25
#include <string.h>
26
#include <time.h>
27
28
0
#define MP2T_SYNC_BYTE      0x47
29
0
#define MP2T_SIZE           188
30
0
#define MP2T_QAM64_BITRATE  26970350    /* bits per second */
31
0
#define MP2T_PCR_CLOCK      27000000    /* cycles per second - 27MHz */
32
33
/* we try to detect trailing data up to 40 bytes after each packet */
34
0
#define TRAILER_LEN_MAX 40
35
36
/* number of consecutive packets we must read to decide that a file
37
   is actually an mpeg2 ts */
38
0
#define SYNC_STEPS   10
39
40
41
typedef struct {
42
    uint64_t bitrate;
43
    uint32_t start_offset;
44
    /* length of header data (e.g., TP_extra_header in BDAV m2ts files) before
45
     * each packet) */
46
    uint8_t header_len;
47
    /* length of trailing data (e.g. FEC) that's appended after each packet */
48
    uint8_t trailer_len;
49
} mp2t_filetype_t;
50
51
static int mp2t_file_type_subtype = -1;
52
53
void register_mp2t(void);
54
55
static bool
56
mp2t_read_packet(wtap *wth, FILE_T fh, int64_t offset, wtap_rec *rec, int *err,
57
                 char **err_info)
58
0
{
59
0
    mp2t_filetype_t *mp2t;
60
0
    uint64_t tmp;
61
62
0
    mp2t = (mp2t_filetype_t*) wth->priv;
63
64
    /*
65
     * MP2T_SIZE will always be less than WTAP_MAX_PACKET_SIZE_STANDARD, so
66
     * we don't have to worry about the packet being too big.
67
     */
68
0
    if (!wtap_read_bytes_or_eof_buffer(fh, &rec->data, MP2T_SIZE, err, err_info))
69
0
        return false;
70
71
0
    wtap_setup_packet_rec(rec, wth->file_encap);
72
0
    rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
73
74
    /* XXX - relative, not absolute, time stamps */
75
0
    rec->presence_flags = WTAP_HAS_TS;
76
77
    /*
78
     * Every packet in an MPEG2-TS stream is has a fixed size of
79
     * MP2T_SIZE plus the number of trailer bytes.
80
     *
81
     * We assume that the bits in the transport stream are supplied at
82
     * a constant rate; is that guaranteed by all media that use
83
     * MPEG2-TS?  If so, the time offset, from the beginning of the
84
     * stream, of a given packet is the packet offset, in bits, divided
85
     * by the bitrate.
86
     *
87
     * It would be really cool to be able to configure the bitrate, in
88
     * case our attempt to guess it from the PCRs of one of the programs
89
     * doesn't get the right answer.
90
     */
91
0
    tmp = ((uint64_t)(offset - mp2t->start_offset) * 8); /* offset, in bits */
92
0
    rec->ts.secs = (time_t)(tmp / mp2t->bitrate);
93
0
    rec->ts.nsecs = (int)((tmp % mp2t->bitrate) * 1000000000 / mp2t->bitrate);
94
95
0
    rec->rec_header.packet_header.caplen = MP2T_SIZE;
96
0
    rec->rec_header.packet_header.len = MP2T_SIZE;
97
98
0
    return true;
99
0
}
100
101
static bool
102
mp2t_read(wtap *wth, wtap_rec *rec, int *err,
103
        char **err_info, int64_t *data_offset)
104
0
{
105
0
    mp2t_filetype_t *mp2t;
106
107
0
    mp2t = (mp2t_filetype_t*) wth->priv;
108
109
    /* if there's a header, skip it and go to the start of the packet */
110
    /* XXX - Eventually we might want to process the header (and trailer?) in
111
     * packet-mp2t.c, in which case we would read it in mp2t_read_packet and
112
     * include header_len in the packet_header lengths. We'd probably want
113
     * pseudo-header information to indicate it to packet-mp2t.c
114
     */
115
0
    if (mp2t->header_len!=0) {
116
0
        if (!wtap_read_bytes_or_eof(wth->fh, NULL, mp2t->header_len, err, err_info)) {
117
0
            return false;
118
0
        }
119
0
    }
120
121
0
    *data_offset = file_tell(wth->fh);
122
123
0
    if (!mp2t_read_packet(wth, wth->fh, *data_offset, rec, err, err_info)) {
124
0
        return false;
125
0
    }
126
127
    /* if there's a trailer, skip it and go to the start of the next packet */
128
0
    if (mp2t->trailer_len!=0) {
129
0
        if (!wtap_read_bytes(wth->fh, NULL, mp2t->trailer_len, err, err_info)) {
130
0
            return false;
131
0
        }
132
0
    }
133
134
0
    return true;
135
0
}
136
137
static bool
138
mp2t_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec,
139
        int *err, char **err_info)
140
0
{
141
0
    if (-1 == file_seek(wth->random_fh, seek_off, SEEK_SET, err)) {
142
0
        return false;
143
0
    }
144
145
0
    if (!mp2t_read_packet(wth, wth->random_fh, seek_off, rec, err, err_info)) {
146
0
        if (*err == 0)
147
0
            *err = WTAP_ERR_SHORT_READ;
148
0
        return false;
149
0
    }
150
0
    return true;
151
0
}
152
153
static uint64_t
154
mp2t_read_pcr(uint8_t *buffer)
155
0
{
156
0
    uint64_t base;
157
0
    uint64_t ext;
158
159
0
    base = pntohu40(buffer);
160
0
    base >>= 7;
161
162
0
    ext = pntohu16(&buffer[4]);
163
0
    ext &= 0x01ff;
164
165
0
    return (base * 300 + ext);
166
0
}
167
168
static bool
169
mp2t_find_next_pcr(wtap *wth, uint8_t trailer_len,
170
        int *err, char **err_info, uint32_t *idx, uint64_t *pcr, uint16_t *pid)
171
0
{
172
0
    uint8_t buffer[MP2T_SIZE+TRAILER_LEN_MAX];
173
0
    bool found;
174
0
    uint8_t afc;
175
0
    unsigned timeout = 0;
176
177
0
    found = false;
178
0
    while (false == found && timeout++ < SYNC_STEPS * SYNC_STEPS) {
179
0
        (*idx)++;
180
0
        if (!wtap_read_bytes_or_eof(
181
0
                    wth->fh, buffer, MP2T_SIZE+trailer_len, err, err_info)) {
182
            /* Read error, short read, or EOF */
183
0
            return false;
184
0
        }
185
186
0
        if (MP2T_SYNC_BYTE != buffer[0]) {
187
0
            continue;
188
0
        }
189
190
        /* Read out the AFC value. */
191
0
        afc = 3 & (buffer[3] >> 4);
192
0
        if (afc < 2) {
193
0
            continue;
194
0
        }
195
196
        /* Check the length. */
197
0
        if (buffer[4] < 7) {
198
0
            continue;
199
0
        }
200
201
        /* Check that there is the PCR flag. */
202
0
        if (0x10 != (0x10 & buffer[5])) {
203
0
            continue;
204
0
        }
205
206
        /* We have a PCR value! */
207
0
        *pcr = mp2t_read_pcr(&buffer[6]);
208
0
        *pid = 0x01ff & pntohu16(&buffer[1]);
209
0
        found = true;
210
0
    }
211
212
0
    return found;
213
0
}
214
215
static wtap_open_return_val
216
mp2t_bits_per_second(wtap *wth, uint32_t first, uint8_t trailer_len,
217
        uint64_t *bitrate, int *err, char **err_info)
218
0
{
219
0
    uint32_t pn1, pn2;
220
0
    uint64_t pcr1, pcr2;
221
0
    uint16_t pid1, pid2;
222
0
    uint32_t idx;
223
0
    uint64_t pcr_delta, bits_passed;
224
225
    /* Find the first PCR + PID.
226
     * Then find another PCR in that PID.
227
     * Take the difference and that's our bitrate.
228
     * All the different PCRs in different PIDs 'should' be the same.
229
     *
230
     * XXX - is this assuming that the time stamps in the PCRs correspond
231
     * to the time scale of the underlying transport stream?
232
     */
233
0
    idx = first;
234
235
0
    if (!mp2t_find_next_pcr(wth, trailer_len, err, err_info, &idx, &pcr1, &pid1)) {
236
        /* Read error, short read, or EOF */
237
0
        if (*err == WTAP_ERR_SHORT_READ)
238
0
            return WTAP_OPEN_NOT_MINE;    /* not a full frame */
239
0
        if (*err != 0)
240
0
            return WTAP_OPEN_ERROR;
241
242
        /* We don't have any PCRs, so we can't guess the bit rate.
243
         * Default to something reasonable.
244
         */
245
0
        *bitrate = MP2T_QAM64_BITRATE;
246
0
        return WTAP_OPEN_MINE;
247
0
    }
248
249
0
    pn1 = idx;
250
0
    pn2 = pn1;
251
252
0
    while (pn1 == pn2) {
253
0
        if (!mp2t_find_next_pcr(wth, trailer_len, err, err_info, &idx, &pcr2, &pid2)) {
254
            /* Read error, short read, or EOF */
255
0
            if (*err == WTAP_ERR_SHORT_READ)
256
0
                return WTAP_OPEN_NOT_MINE;    /* not a full frame */
257
0
            if (*err != 0)
258
0
                return WTAP_OPEN_ERROR;
259
260
            /* We don't have two PCRs for the same PID, so we can't guess
261
             * the bit rate.
262
             * Default to something reasonable.
263
             */
264
0
            *bitrate = MP2T_QAM64_BITRATE;
265
0
            return WTAP_OPEN_MINE;
266
0
        }
267
268
0
        if (pid1 == pid2) {
269
0
            pn2 = idx;
270
0
        }
271
0
    }
272
273
0
    if (pcr2 <= pcr1) {
274
        /* The PCRs for that PID didn't go forward; treat that as an
275
         * indication that this isn't an MPEG-2 TS.
276
         */
277
0
        return WTAP_OPEN_NOT_MINE;
278
0
    }
279
0
    pcr_delta = pcr2 - pcr1;
280
    /* cast one of the factors to uint64_t
281
       otherwise, the multiplication would use uint32_t and could
282
       overflow before the result is assigned to the uint64_t bits_passed */
283
0
    bits_passed = (uint64_t)MP2T_SIZE * (pn2 - pn1) * 8;
284
285
0
    *bitrate = ((MP2T_PCR_CLOCK * bits_passed) / pcr_delta);
286
0
    if (*bitrate == 0) {
287
        /* pcr_delta < MP2T_PCR_CLOCK * bits_passed (pn2 != pn1,
288
         * as that's the test for the loop above, so bits_passed
289
         * is non-zero).
290
         *
291
         * That will produce a fractional bitrate, which turns
292
         * into zero, causing a zero divide later.
293
         *
294
         * XXX - should we report this as "not ours"?  A bitrate
295
         * of less than 1 bit per second is not very useful for any
296
         * form of audio/video, so presumably that's unlikely to
297
         * be an MP2T file.
298
         */
299
0
        return WTAP_OPEN_ERROR;
300
0
    }
301
0
    return WTAP_OPEN_MINE;
302
0
}
303
304
wtap_open_return_val
305
mp2t_open(wtap *wth, int *err, char **err_info)
306
0
{
307
0
    uint8_t buffer[MP2T_SIZE+TRAILER_LEN_MAX];
308
0
    uint8_t trailer_len = 0;
309
0
    uint8_t header_len = 0;
310
0
    unsigned sync_steps = 0;
311
0
    unsigned i;
312
0
    uint32_t first = 0;
313
0
    mp2t_filetype_t *mp2t;
314
0
    wtap_open_return_val status;
315
0
    uint64_t bitrate;
316
317
318
0
    if (!wtap_read_bytes(wth->fh, buffer, MP2T_SIZE, err, err_info)) {
319
0
        if (*err != WTAP_ERR_SHORT_READ)
320
0
            return WTAP_OPEN_ERROR;
321
0
        return WTAP_OPEN_NOT_MINE;
322
0
    }
323
324
0
    for (i = 0; i < MP2T_SIZE; i++) {
325
0
        if (MP2T_SYNC_BYTE == buffer[i]) {
326
0
            first = i;
327
0
            goto found;
328
0
        }
329
0
    }
330
    /*
331
     * No sync bytes found, so not an MPEG-2 Transport Stream file.
332
     */
333
0
    return WTAP_OPEN_NOT_MINE; /* wrong file type - not an mpeg2 ts file */
334
335
0
found:
336
0
    if (-1 == file_seek(wth->fh, first, SEEK_SET, err)) {
337
0
        return WTAP_OPEN_ERROR;
338
0
    }
339
340
    /* read some packets and make sure they all start with a sync byte */
341
0
    do {
342
0
        if (!wtap_read_bytes(wth->fh, buffer, MP2T_SIZE+trailer_len, err, err_info)) {
343
0
            if (*err != WTAP_ERR_SHORT_READ)
344
0
                return WTAP_OPEN_ERROR;  /* read error */
345
0
            if(sync_steps<2) return WTAP_OPEN_NOT_MINE; /* wrong file type - not an mpeg2 ts file */
346
0
            break;  /* end of file, that's ok if we're still in sync */
347
0
        }
348
0
        if (buffer[0] == MP2T_SYNC_BYTE) {
349
0
                sync_steps++;
350
0
        }
351
0
        else {
352
            /* no sync byte found, check if trailing data is appended
353
               and we have to increase the packet size */
354
355
            /* if we've already detected a trailer field, we must remain in sync
356
               another mismatch means we have no mpeg2 ts file */
357
0
            if (trailer_len>0) {
358
                /* check for header with spurious sync byte in header */
359
0
                if (first < trailer_len) {
360
0
                    first += 1;
361
0
                    trailer_len -= 1;
362
0
                    if (-1 == file_seek(wth->fh, first, SEEK_SET, err)) {
363
0
                        return WTAP_OPEN_ERROR;
364
0
                    }
365
                    /* Shouldn't fail, we just read this */
366
0
                    if (!wtap_read_bytes(wth->fh, buffer, MP2T_SIZE, err, err_info)) {
367
0
                        if (*err != WTAP_ERR_SHORT_READ)
368
0
                            return WTAP_OPEN_ERROR;
369
0
                        return WTAP_OPEN_NOT_MINE;
370
0
                    }
371
0
                    for (i = 0; i < trailer_len; i++) {
372
0
                        if (MP2T_SYNC_BYTE == buffer[i]) {
373
0
                            first += i;
374
0
                            trailer_len -= i;
375
0
                            goto found;
376
0
                        }
377
0
                    }
378
0
                }
379
0
                return WTAP_OPEN_NOT_MINE;
380
0
            }
381
382
            /* check if a trailer is appended to the packet */
383
0
            for (i=0; i<TRAILER_LEN_MAX; i++) {
384
0
                if (buffer[i] == MP2T_SYNC_BYTE) {
385
0
                    trailer_len = i;
386
0
                    if (-1 == file_seek(wth->fh, first, SEEK_SET, err)) {
387
0
                        return WTAP_OPEN_ERROR;
388
0
                    }
389
0
                    sync_steps = 0;
390
0
                    break;
391
0
                }
392
0
            }
393
            /* no sync byte found in the vicinity, this is no mpeg2 ts file */
394
0
            if (i==TRAILER_LEN_MAX)
395
0
                return WTAP_OPEN_NOT_MINE;
396
0
        }
397
0
    } while (sync_steps < SYNC_STEPS);
398
399
0
    if (-1 == file_seek(wth->fh, first, SEEK_SET, err)) {
400
0
        return WTAP_OPEN_ERROR;
401
0
    }
402
403
    /* Ensure there is a valid bitrate */
404
0
    status = mp2t_bits_per_second(wth, first, trailer_len,
405
0
            &bitrate, err, err_info);
406
0
    if (status != WTAP_OPEN_MINE) {
407
0
        return status;
408
0
    }
409
410
    /* If the packet didn't start on a sync byte, the "trailer" might
411
     * be a header. At least BDAV M2TS does this with a four byte header. */
412
0
    header_len = MIN(first, trailer_len);
413
0
    first -= header_len;
414
0
    trailer_len -= header_len;
415
416
0
    if (-1 == file_seek(wth->fh, first, SEEK_SET, err)) {
417
0
        return WTAP_OPEN_ERROR;
418
0
    }
419
420
0
    wth->file_type_subtype = mp2t_file_type_subtype;
421
0
    wth->file_encap = WTAP_ENCAP_MPEG_2_TS;
422
0
    wth->file_tsprec = WTAP_TSPREC_NSEC;
423
0
    wth->subtype_read = mp2t_read;
424
0
    wth->subtype_seek_read = mp2t_seek_read;
425
0
    wth->snapshot_length = 0;
426
427
0
    mp2t = g_new(mp2t_filetype_t, 1);
428
429
0
    wth->priv = mp2t;
430
0
    mp2t->start_offset = first;
431
0
    mp2t->trailer_len = trailer_len;
432
0
    mp2t->header_len = header_len;
433
0
    mp2t->bitrate = bitrate;
434
435
0
    return WTAP_OPEN_MINE;
436
0
}
437
438
static int mp2t_dump_can_write_encap(int encap)
439
0
{
440
    /* Per-packet encapsulations aren't supported. */
441
0
    if (encap == WTAP_ENCAP_PER_PACKET) {
442
0
        return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
443
0
    }
444
445
    /* This is the only encapsulation type we write. */
446
0
    if (encap != WTAP_ENCAP_MPEG_2_TS) {
447
0
        return WTAP_ERR_UNWRITABLE_ENCAP;
448
0
    }
449
450
0
    return 0;
451
0
}
452
453
/* Write a record for a packet to a dump file.
454
   Returns true on success, false on failure. */
455
static bool mp2t_dump(wtap_dumper *wdh, const wtap_rec *rec,
456
    int *err, char **err_info _U_)
457
0
{
458
    /* We can only write packet records. */
459
0
    if (rec->rec_type != REC_TYPE_PACKET) {
460
0
        *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
461
0
        *err_info = wtap_unwritable_rec_type_err_string(rec);
462
0
        return false;
463
0
    }
464
465
    /*
466
     * Make sure this packet doesn't have a link-layer type that
467
     * differs from the one for the file.
468
     */
469
0
    if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
470
0
        *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
471
0
        return false;
472
0
    }
473
474
    /* A MPEG-2 Transport Stream is just the packet bytes, with no header.
475
     * The sync byte is supposed to identify where packets start.
476
     * Note this drops existing headers and trailers currently, since we
477
     * don't include them in the record.
478
     */
479
0
    if (!wtap_dump_file_write(wdh, ws_buffer_start_ptr(&rec->data), rec->rec_header.packet_header.caplen, err)) {
480
0
        return false;
481
0
    }
482
483
0
    return true;
484
0
}
485
486
/* Returns true on success, false on failure; sets "*err" to an error code on
487
   failure */
488
static bool mp2t_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_)
489
0
{
490
    /* There is no header, so we just always return true. */
491
0
    wdh->subtype_write = mp2t_dump;
492
493
0
    return true;
494
0
}
495
496
static const struct supported_block_type mp2t_blocks_supported[] = {
497
    /*
498
     * We support packet blocks, with no comments or other options.
499
     */
500
    { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
501
};
502
503
static const struct file_type_subtype_info mp2t_info = {
504
    "MPEG2 transport stream", "mp2t", "mp2t", "ts;m2ts;mpg",
505
    false, BLOCKS_SUPPORTED(mp2t_blocks_supported),
506
    mp2t_dump_can_write_encap, mp2t_dump_open, NULL
507
};
508
509
void register_mp2t(void)
510
15
{
511
15
    mp2t_file_type_subtype = wtap_register_file_type_subtype(&mp2t_info);
512
513
    /*
514
     * Register name for backwards compatibility with the
515
     * wtap_filetypes table in Lua.
516
     */
517
15
    wtap_register_backwards_compatibility_lua_name("MPEG_2_TS",
518
15
                                                   mp2t_file_type_subtype);
519
15
}
520
521
/*
522
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
523
 *
524
 * Local variables:
525
 * c-basic-offset: 4
526
 * tab-width: 8
527
 * indent-tabs-mode: nil
528
 * End:
529
 *
530
 * vi: set shiftwidth=4 tabstop=8 expandtab:
531
 * :indentSize=4:tabSize=8:noTabs=true:
532
 */