Revert "UNUSED enc_key_id_{equal,hash}"
[wireshark-sm.git] / wiretap / mp2t.c
blob14189970e3b939757917583675a0bae402521e04
1 /* mp2t.c
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
7 * Wiretap Library
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
11 #include "config.h"
12 #include "mp2t.h"
14 #include <sys/types.h>
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
20 #include "wtap-int.h"
21 #include <wsutil/buffer.h>
22 #include "file_wrappers.h"
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
27 #define MP2T_SYNC_BYTE 0x47
28 #define MP2T_SIZE 188
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 */
37 #define SYNC_STEPS 10
40 typedef struct {
41 uint64_t bitrate;
42 uint32_t start_offset;
43 /* length of header data (e.g., TP_extra_header in BDAV m2ts files) before
44 * each packet) */
45 uint8_t header_len;
46 /* length of trailing data (e.g. FEC) that's appended after each packet */
47 uint8_t trailer_len;
48 } mp2t_filetype_t;
50 static int mp2t_file_type_subtype = -1;
52 void register_mp2t(void);
54 static bool
55 mp2t_read_packet(mp2t_filetype_t *mp2t, FILE_T fh, int64_t offset,
56 wtap_rec *rec, Buffer *buf, int *err,
57 char **err_info)
59 uint64_t tmp;
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))
67 return false;
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
83 * by the bitrate.
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;
96 return true;
99 static bool
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)) {
115 return false;
119 *data_offset = file_tell(wth->fh);
121 if (!mp2t_read_packet(mp2t, wth->fh, *data_offset, rec, buf, err,
122 err_info)) {
123 return false;
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)) {
129 return false;
133 return true;
136 static bool
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)) {
143 return false;
146 mp2t = (mp2t_filetype_t*) wth->priv;
148 if (!mp2t_read_packet(mp2t, wth->random_fh, seek_off, rec, buf,
149 err, err_info)) {
150 if (*err == 0)
151 *err = WTAP_ERR_SHORT_READ;
152 return false;
154 return true;
157 static uint64_t
158 mp2t_read_pcr(uint8_t *buffer)
160 uint64_t base;
161 uint64_t ext;
163 base = pntoh40(buffer);
164 base >>= 7;
166 ext = pntoh16(&buffer[4]);
167 ext &= 0x01ff;
169 return (base * 300 + ext);
172 static bool
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];
177 bool found;
178 uint8_t afc;
179 unsigned timeout = 0;
181 found = false;
182 while (false == found && timeout++ < SYNC_STEPS * SYNC_STEPS) {
183 (*idx)++;
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 */
187 return false;
190 if (MP2T_SYNC_BYTE != buffer[0]) {
191 continue;
194 /* Read out the AFC value. */
195 afc = 3 & (buffer[3] >> 4);
196 if (afc < 2) {
197 continue;
200 /* Check the length. */
201 if (buffer[4] < 7) {
202 continue;
205 /* Check that there is the PCR flag. */
206 if (0x10 != (0x10 & buffer[5])) {
207 continue;
210 /* We have a PCR value! */
211 *pcr = mp2t_read_pcr(&buffer[6]);
212 *pid = 0x01ff & pntoh16(&buffer[1]);
213 found = true;
216 return found;
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)
223 uint32_t pn1, pn2;
224 uint64_t pcr1, pcr2;
225 uint16_t pid1, pid2;
226 uint32_t idx;
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?
237 idx = first;
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 */
243 if (*err != 0)
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;
253 pn1 = idx;
254 pn2 = pn1;
256 while (pn1 == pn2) {
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 */
261 if (*err != 0)
262 return WTAP_OPEN_ERROR;
264 /* We don't have two PCRs for the same PID, so we can't guess
265 * the bit rate.
266 * Default to something reasonable.
268 *bitrate = MP2T_QAM64_BITRATE;
269 return WTAP_OPEN_MINE;
272 if (pid1 == pid2) {
273 pn2 = idx;
277 if (pcr2 <= pcr1) {
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);
290 if (*bitrate == 0) {
291 /* pcr_delta < MP2T_PCR_CLOCK * bits_passed (pn2 != pn1,
292 * as that's the test for the loop above, so bits_passed
293 * is non-zero).
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
301 * be an MP2T file.
303 return WTAP_OPEN_ERROR;
305 return WTAP_OPEN_MINE;
308 wtap_open_return_val
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;
315 unsigned i;
316 uint32_t first = 0;
317 mp2t_filetype_t *mp2t;
318 wtap_open_return_val status;
319 uint64_t bitrate;
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]) {
330 first = i;
331 goto found;
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 */
339 found:
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 */
345 do {
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) {
353 sync_steps++;
355 else {
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 */
361 if (trailer_len>0) {
362 /* check for header with spurious sync byte in header */
363 if (first < trailer_len) {
364 first += 1;
365 trailer_len -= 1;
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]) {
377 first += i;
378 trailer_len -= i;
379 goto found;
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) {
389 trailer_len = i;
390 if (-1 == file_seek(wth->fh, first, SEEK_SET, err)) {
391 return WTAP_OPEN_ERROR;
393 sync_steps = 0;
394 break;
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) {
411 return status;
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);
417 first -= header_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);
433 wth->priv = mp2t;
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;
454 return 0;
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;
465 return false;
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;
474 return false;
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)) {
483 return false;
486 return true;
489 /* Returns true on success, false on failure; sets "*err" to an error code on
490 failure */
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;
496 return true;
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
527 * Local variables:
528 * c-basic-offset: 4
529 * tab-width: 8
530 * indent-tabs-mode: nil
531 * End:
533 * vi: set shiftwidth=4 tabstop=8 expandtab:
534 * :indentSize=4:tabSize=8:noTabs=true: