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
8 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include <sys/types.h>
21 #include <wsutil/buffer.h>
22 #include "file_wrappers.h"
27 #define MP2T_SYNC_BYTE 0x47
29 #define MP2T_QAM64_BITRATE 26970350 /* bits per second */
30 #define MP2T_PCR_CLOCK 27000000 /* cycles per second - 27MHz */
32 /* we try to detect trailing data up to 40 bytes after each packet */
33 #define TRAILER_LEN_MAX 40
35 /* number of consecutive packets we must read to decide that a file
36 is actually an mpeg2 ts */
42 uint32_t start_offset
;
43 /* length of header data (e.g., TP_extra_header in BDAV m2ts files) before
46 /* length of trailing data (e.g. FEC) that's appended after each packet */
50 static int mp2t_file_type_subtype
= -1;
52 void register_mp2t(void);
55 mp2t_read_packet(mp2t_filetype_t
*mp2t
, FILE_T fh
, int64_t offset
,
56 wtap_rec
*rec
, Buffer
*buf
, int *err
,
62 * MP2T_SIZE will always be less than WTAP_MAX_PACKET_SIZE_STANDARD, so
63 * we don't have to worry about the packet being too big.
65 ws_buffer_assure_space(buf
, MP2T_SIZE
);
66 if (!wtap_read_bytes_or_eof(fh
, ws_buffer_start_ptr(buf
), MP2T_SIZE
, err
, err_info
))
69 rec
->rec_type
= REC_TYPE_PACKET
;
70 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
72 /* XXX - relative, not absolute, time stamps */
73 rec
->presence_flags
= WTAP_HAS_TS
;
76 * Every packet in an MPEG2-TS stream is has a fixed size of
77 * MP2T_SIZE plus the number of trailer bytes.
79 * We assume that the bits in the transport stream are supplied at
80 * a constant rate; is that guaranteed by all media that use
81 * MPEG2-TS? If so, the time offset, from the beginning of the
82 * stream, of a given packet is the packet offset, in bits, divided
85 * It would be really cool to be able to configure the bitrate, in
86 * case our attempt to guess it from the PCRs of one of the programs
87 * doesn't get the right answer.
89 tmp
= ((uint64_t)(offset
- mp2t
->start_offset
) * 8); /* offset, in bits */
90 rec
->ts
.secs
= (time_t)(tmp
/ mp2t
->bitrate
);
91 rec
->ts
.nsecs
= (int)((tmp
% mp2t
->bitrate
) * 1000000000 / mp2t
->bitrate
);
93 rec
->rec_header
.packet_header
.caplen
= MP2T_SIZE
;
94 rec
->rec_header
.packet_header
.len
= MP2T_SIZE
;
100 mp2t_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
, int *err
,
101 char **err_info
, int64_t *data_offset
)
103 mp2t_filetype_t
*mp2t
;
105 mp2t
= (mp2t_filetype_t
*) wth
->priv
;
107 /* if there's a header, skip it and go to the start of the packet */
108 /* XXX - Eventually we might want to process the header (and trailer?) in
109 * packet-mp2t.c, in which case we would read it in mp2t_read_packet and
110 * include header_len in the packet_header lengths. We'd probably want
111 * pseudo-header information to indicate it to packet-mp2t.c
113 if (mp2t
->header_len
!=0) {
114 if (!wtap_read_bytes_or_eof(wth
->fh
, NULL
, mp2t
->header_len
, err
, err_info
)) {
119 *data_offset
= file_tell(wth
->fh
);
121 if (!mp2t_read_packet(mp2t
, wth
->fh
, *data_offset
, rec
, buf
, err
,
126 /* if there's a trailer, skip it and go to the start of the next packet */
127 if (mp2t
->trailer_len
!=0) {
128 if (!wtap_read_bytes(wth
->fh
, NULL
, mp2t
->trailer_len
, err
, err_info
)) {
137 mp2t_seek_read(wtap
*wth
, int64_t seek_off
, wtap_rec
*rec
,
138 Buffer
*buf
, int *err
, char **err_info
)
140 mp2t_filetype_t
*mp2t
;
142 if (-1 == file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
)) {
146 mp2t
= (mp2t_filetype_t
*) wth
->priv
;
148 if (!mp2t_read_packet(mp2t
, wth
->random_fh
, seek_off
, rec
, buf
,
151 *err
= WTAP_ERR_SHORT_READ
;
158 mp2t_read_pcr(uint8_t *buffer
)
163 base
= pntoh40(buffer
);
166 ext
= pntoh16(&buffer
[4]);
169 return (base
* 300 + ext
);
173 mp2t_find_next_pcr(wtap
*wth
, uint8_t trailer_len
,
174 int *err
, char **err_info
, uint32_t *idx
, uint64_t *pcr
, uint16_t *pid
)
176 uint8_t buffer
[MP2T_SIZE
+TRAILER_LEN_MAX
];
179 unsigned timeout
= 0;
182 while (false == found
&& timeout
++ < SYNC_STEPS
* SYNC_STEPS
) {
184 if (!wtap_read_bytes_or_eof(
185 wth
->fh
, buffer
, MP2T_SIZE
+trailer_len
, err
, err_info
)) {
186 /* Read error, short read, or EOF */
190 if (MP2T_SYNC_BYTE
!= buffer
[0]) {
194 /* Read out the AFC value. */
195 afc
= 3 & (buffer
[3] >> 4);
200 /* Check the length. */
205 /* Check that there is the PCR flag. */
206 if (0x10 != (0x10 & buffer
[5])) {
210 /* We have a PCR value! */
211 *pcr
= mp2t_read_pcr(&buffer
[6]);
212 *pid
= 0x01ff & pntoh16(&buffer
[1]);
219 static wtap_open_return_val
220 mp2t_bits_per_second(wtap
*wth
, uint32_t first
, uint8_t trailer_len
,
221 uint64_t *bitrate
, int *err
, char **err_info
)
227 uint64_t pcr_delta
, bits_passed
;
229 /* Find the first PCR + PID.
230 * Then find another PCR in that PID.
231 * Take the difference and that's our bitrate.
232 * All the different PCRs in different PIDs 'should' be the same.
234 * XXX - is this assuming that the time stamps in the PCRs correspond
235 * to the time scale of the underlying transport stream?
239 if (!mp2t_find_next_pcr(wth
, trailer_len
, err
, err_info
, &idx
, &pcr1
, &pid1
)) {
240 /* Read error, short read, or EOF */
241 if (*err
== WTAP_ERR_SHORT_READ
)
242 return WTAP_OPEN_NOT_MINE
; /* not a full frame */
244 return WTAP_OPEN_ERROR
;
246 /* We don't have any PCRs, so we can't guess the bit rate.
247 * Default to something reasonable.
249 *bitrate
= MP2T_QAM64_BITRATE
;
250 return WTAP_OPEN_MINE
;
257 if (!mp2t_find_next_pcr(wth
, trailer_len
, err
, err_info
, &idx
, &pcr2
, &pid2
)) {
258 /* Read error, short read, or EOF */
259 if (*err
== WTAP_ERR_SHORT_READ
)
260 return WTAP_OPEN_NOT_MINE
; /* not a full frame */
262 return WTAP_OPEN_ERROR
;
264 /* We don't have two PCRs for the same PID, so we can't guess
266 * Default to something reasonable.
268 *bitrate
= MP2T_QAM64_BITRATE
;
269 return WTAP_OPEN_MINE
;
278 /* The PCRs for that PID didn't go forward; treat that as an
279 * indication that this isn't an MPEG-2 TS.
281 return WTAP_OPEN_NOT_MINE
;
283 pcr_delta
= pcr2
- pcr1
;
284 /* cast one of the factors to uint64_t
285 otherwise, the multiplication would use uint32_t and could
286 overflow before the result is assigned to the uint64_t bits_passed */
287 bits_passed
= (uint64_t)MP2T_SIZE
* (pn2
- pn1
) * 8;
289 *bitrate
= ((MP2T_PCR_CLOCK
* bits_passed
) / pcr_delta
);
291 /* pcr_delta < MP2T_PCR_CLOCK * bits_passed (pn2 != pn1,
292 * as that's the test for the loop above, so bits_passed
295 * That will produce a fractional bitrate, which turns
296 * into zero, causing a zero divide later.
298 * XXX - should we report this as "not ours"? A bitrate
299 * of less than 1 bit per second is not very useful for any
300 * form of audio/video, so presumably that's unlikely to
303 return WTAP_OPEN_ERROR
;
305 return WTAP_OPEN_MINE
;
309 mp2t_open(wtap
*wth
, int *err
, char **err_info
)
311 uint8_t buffer
[MP2T_SIZE
+TRAILER_LEN_MAX
];
312 uint8_t trailer_len
= 0;
313 uint8_t header_len
= 0;
314 unsigned sync_steps
= 0;
317 mp2t_filetype_t
*mp2t
;
318 wtap_open_return_val status
;
322 if (!wtap_read_bytes(wth
->fh
, buffer
, MP2T_SIZE
, err
, err_info
)) {
323 if (*err
!= WTAP_ERR_SHORT_READ
)
324 return WTAP_OPEN_ERROR
;
325 return WTAP_OPEN_NOT_MINE
;
328 for (i
= 0; i
< MP2T_SIZE
; i
++) {
329 if (MP2T_SYNC_BYTE
== buffer
[i
]) {
335 * No sync bytes found, so not an MPEG-2 Transport Stream file.
337 return WTAP_OPEN_NOT_MINE
; /* wrong file type - not an mpeg2 ts file */
340 if (-1 == file_seek(wth
->fh
, first
, SEEK_SET
, err
)) {
341 return WTAP_OPEN_ERROR
;
344 /* read some packets and make sure they all start with a sync byte */
346 if (!wtap_read_bytes(wth
->fh
, buffer
, MP2T_SIZE
+trailer_len
, err
, err_info
)) {
347 if (*err
!= WTAP_ERR_SHORT_READ
)
348 return WTAP_OPEN_ERROR
; /* read error */
349 if(sync_steps
<2) return WTAP_OPEN_NOT_MINE
; /* wrong file type - not an mpeg2 ts file */
350 break; /* end of file, that's ok if we're still in sync */
352 if (buffer
[0] == MP2T_SYNC_BYTE
) {
356 /* no sync byte found, check if trailing data is appended
357 and we have to increase the packet size */
359 /* if we've already detected a trailer field, we must remain in sync
360 another mismatch means we have no mpeg2 ts file */
362 /* check for header with spurious sync byte in header */
363 if (first
< trailer_len
) {
366 if (-1 == file_seek(wth
->fh
, first
, SEEK_SET
, err
)) {
367 return WTAP_OPEN_ERROR
;
369 /* Shouldn't fail, we just read this */
370 if (!wtap_read_bytes(wth
->fh
, buffer
, MP2T_SIZE
, err
, err_info
)) {
371 if (*err
!= WTAP_ERR_SHORT_READ
)
372 return WTAP_OPEN_ERROR
;
373 return WTAP_OPEN_NOT_MINE
;
375 for (i
= 0; i
< trailer_len
; i
++) {
376 if (MP2T_SYNC_BYTE
== buffer
[i
]) {
383 return WTAP_OPEN_NOT_MINE
;
386 /* check if a trailer is appended to the packet */
387 for (i
=0; i
<TRAILER_LEN_MAX
; i
++) {
388 if (buffer
[i
] == MP2T_SYNC_BYTE
) {
390 if (-1 == file_seek(wth
->fh
, first
, SEEK_SET
, err
)) {
391 return WTAP_OPEN_ERROR
;
397 /* no sync byte found in the vicinity, this is no mpeg2 ts file */
398 if (i
==TRAILER_LEN_MAX
)
399 return WTAP_OPEN_NOT_MINE
;
401 } while (sync_steps
< SYNC_STEPS
);
403 if (-1 == file_seek(wth
->fh
, first
, SEEK_SET
, err
)) {
404 return WTAP_OPEN_ERROR
;
407 /* Ensure there is a valid bitrate */
408 status
= mp2t_bits_per_second(wth
, first
, trailer_len
,
409 &bitrate
, err
, err_info
);
410 if (status
!= WTAP_OPEN_MINE
) {
414 /* If the packet didn't start on a sync byte, the "trailer" might
415 * be a header. At least BDAV M2TS does this with a four byte header. */
416 header_len
= MIN(first
, trailer_len
);
418 trailer_len
-= header_len
;
420 if (-1 == file_seek(wth
->fh
, first
, SEEK_SET
, err
)) {
421 return WTAP_OPEN_ERROR
;
424 wth
->file_type_subtype
= mp2t_file_type_subtype
;
425 wth
->file_encap
= WTAP_ENCAP_MPEG_2_TS
;
426 wth
->file_tsprec
= WTAP_TSPREC_NSEC
;
427 wth
->subtype_read
= mp2t_read
;
428 wth
->subtype_seek_read
= mp2t_seek_read
;
429 wth
->snapshot_length
= 0;
431 mp2t
= g_new(mp2t_filetype_t
, 1);
434 mp2t
->start_offset
= first
;
435 mp2t
->trailer_len
= trailer_len
;
436 mp2t
->header_len
= header_len
;
437 mp2t
->bitrate
= bitrate
;
439 return WTAP_OPEN_MINE
;
442 static int mp2t_dump_can_write_encap(int encap
)
444 /* Per-packet encapsulations aren't supported. */
445 if (encap
== WTAP_ENCAP_PER_PACKET
) {
446 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED
;
449 /* This is the only encapsulation type we write. */
450 if (encap
!= WTAP_ENCAP_MPEG_2_TS
) {
451 return WTAP_ERR_UNWRITABLE_ENCAP
;
457 /* Write a record for a packet to a dump file.
458 Returns true on success, false on failure. */
459 static bool mp2t_dump(wtap_dumper
*wdh
, const wtap_rec
*rec
,
460 const uint8_t *pd
, int *err
, char **err_info _U_
)
462 /* We can only write packet records. */
463 if (rec
->rec_type
!= REC_TYPE_PACKET
) {
464 *err
= WTAP_ERR_UNWRITABLE_REC_TYPE
;
469 * Make sure this packet doesn't have a link-layer type that
470 * differs from the one for the file.
472 if (wdh
->file_encap
!= rec
->rec_header
.packet_header
.pkt_encap
) {
473 *err
= WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED
;
477 /* A MPEG-2 Transport Stream is just the packet bytes, with no header.
478 * The sync byte is supposed to identify where packets start.
479 * Note this drops existing headers and trailers currently, since we
480 * don't include them in the record.
482 if (!wtap_dump_file_write(wdh
, pd
, rec
->rec_header
.packet_header
.caplen
, err
)) {
489 /* Returns true on success, false on failure; sets "*err" to an error code on
491 static bool mp2t_dump_open(wtap_dumper
*wdh
, int *err _U_
, char **err_info _U_
)
493 /* There is no header, so we just always return true. */
494 wdh
->subtype_write
= mp2t_dump
;
499 static const struct supported_block_type mp2t_blocks_supported
[] = {
501 * We support packet blocks, with no comments or other options.
503 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, NO_OPTIONS_SUPPORTED
}
506 static const struct file_type_subtype_info mp2t_info
= {
507 "MPEG2 transport stream", "mp2t", "mp2t", "ts;m2ts;mpg",
508 false, BLOCKS_SUPPORTED(mp2t_blocks_supported
),
509 mp2t_dump_can_write_encap
, mp2t_dump_open
, NULL
512 void register_mp2t(void)
514 mp2t_file_type_subtype
= wtap_register_file_type_subtype(&mp2t_info
);
517 * Register name for backwards compatibility with the
518 * wtap_filetypes table in Lua.
520 wtap_register_backwards_compatibility_lua_name("MPEG_2_TS",
521 mp2t_file_type_subtype
);
525 * Editor modelines - https://www.wireshark.org/tools/modelines.html
530 * indent-tabs-mode: nil
533 * vi: set shiftwidth=4 tabstop=8 expandtab:
534 * :indentSize=4:tabSize=8:noTabs=true: