Wireshark-dev: Re: [Wireshark-dev] [PATCH 1/2] wiretap: New MPEG file format

From: "Shaun Jackman" <sjackman@xxxxxxxxx>
Date: Fri, 16 Mar 2007 14:03:17 -0600
On 3/15/07, Jeff Morriss <jeff.morriss@xxxxxxxxxxx> wrote:
Actually the patch isn't attached ;-)

Grumble. =P
2007-03-15  Shaun Jackman  <sjackman@xxxxxxxxx>

       * wiretap/Makefile.common (NONGENERATED_C_FILES): Add mpeg.c.
       (NONGENERATED_HEADER_FILES): Add mpeg.h.
       * wiretap/file_access.c (open_routines): Add mpeg_open.
       (dump_open_table): Add mpeg.
       * wiretap/mpeg.c: New file.
       * wiretap/mpeg.h: Ditto.
       * wiretap/mpeg-audio.c: Ditto.
       * wiretap/mpeg-audio.h: Ditto.
       * wiretap/wtap.c (encap_table): Add mpeg.
       * wiretap/wtap.h (WTAP_ENCAP_MPEG, WTAP_FILE_MPEG): New.
       (WTAP_NUM_ENCAP_TYPES, WTAP_NUM_FILE_TYPES): Increase.

Index: wiretap/mpeg-audio.c
===================================================================
--- wiretap/mpeg-audio.c	(revision 0)
+++ wiretap/mpeg-audio.c	(revision 0)
@@ -0,0 +1,44 @@
+/* MPEG Audio header dissection
+ * Written by Shaun Jackman <sjackman@xxxxxxxxx>
+ * Copyright 2007 Shaun Jackman
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ */
+
+#include "mpeg-audio.h"
+
+const int mpa_versions[4] = { 2, -1, 1, 0 };
+const int mpa_layers[4] = { -1, 2, 1, 0 };
+
+const unsigned mpa_samples[3][3] = {
+	{ 384, 1152, 1152 },
+	{ 384, 1152, 576 },
+	{ 384, 1152, 576 },
+};
+
+const unsigned mpa_bitrates[3][3][16] = { /* kb/s */
+  {
+	{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 },
+	{ 0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384 },
+	{ 0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320 },
+  },
+  {
+	{ 0, 32, 48, 56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256 },
+	{ 0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160 },
+	{ 0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160 },
+  },
+  {
+	{ 0, 32, 48, 56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256 },
+	{ 0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160 },
+	{ 0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160 },
+  },
+};
+
+const unsigned mpa_frequencies[3][4] = {
+	{ 44100, 48000, 32000 },
+	{ 22050, 24000, 16000 },
+	{ 11025, 12000, 8000 },
+};
+
+const unsigned mpa_padding[3] = { 4, 1, 1 };
Index: wiretap/mpeg-audio.h
===================================================================
--- wiretap/mpeg-audio.h	(revision 0)
+++ wiretap/mpeg-audio.h	(revision 0)
@@ -0,0 +1,94 @@
+/* MPEG Audio header dissection
+ * Written by Shaun Jackman <sjackman@xxxxxxxxx>
+ * Copyright 2007 Shaun Jackman
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ */
+
+#ifndef MPA_H
+#define MPA_H 1
+
+struct mpa {
+	unsigned emphasis   :2;
+	unsigned original   :1;
+	unsigned copyright  :1;
+	unsigned modeext    :2;
+	unsigned mode       :2;
+	unsigned private    :1;
+	unsigned padding    :1;
+	unsigned frequency  :2;
+	unsigned bitrate    :4;
+	unsigned protection :1;
+	unsigned layer      :2;
+	unsigned version    :2;
+	unsigned sync       :11;
+};
+
+#define MPA_MARSHAL_SYNC(n)       ((n) >> 21 & 0x7ff)
+#define MPA_MARSHAL_VERSION(n)    ((n) >> 19 & 0x3)
+#define MPA_MARSHAL_LAYER(n)      ((n) >> 17 & 0x3)
+#define MPA_MARSHAL_PROTECTION(n) ((n) >> 16 & 0x1)
+#define MPA_MARSHAL_BITRATE(n)    ((n) >> 12 & 0xf)
+#define MPA_MARSHAL_FREQUENCY(n)  ((n) >> 10 & 0x3)
+#define MPA_MARSHAL_PADDING(n)    ((n) >>  9 & 0x1)
+#define MPA_MARSHAL_PRIVATE(n)    ((n) >>  8 & 0x1)
+#define MPA_MARSHAL_MODE(n)       ((n) >>  6 & 0x3)
+#define MPA_MARSHAL_MODEEXT(n)    ((n) >>  4 & 0x3)
+#define MPA_MARSHAL_COPYRIGHT(n)  ((n) >>  3 & 0x1)
+#define MPA_MARSHAL_ORIGINAL(n)   ((n) >>  2 & 0x1)
+#define MPA_MARSHAL_EMPHASIS(n)   ((n) >>  0 & 0x3)
+
+#define MPA_MARSHAL(mpa, n) do { \
+	(mpa)->sync       = MPA_MARSHAL_SYNC(n);       \
+	(mpa)->version    = MPA_MARSHAL_VERSION(n);    \
+	(mpa)->layer      = MPA_MARSHAL_LAYER(n);      \
+	(mpa)->protection = MPA_MARSHAL_PROTECTION(n); \
+	(mpa)->bitrate    = MPA_MARSHAL_BITRATE(n);    \
+	(mpa)->frequency  = MPA_MARSHAL_FREQUENCY(n);  \
+	(mpa)->padding    = MPA_MARSHAL_PADDING(n);    \
+	(mpa)->private    = MPA_MARSHAL_PRIVATE(n);    \
+	(mpa)->mode       = MPA_MARSHAL_MODE(n);       \
+	(mpa)->modeext    = MPA_MARSHAL_MODEEXT(n);    \
+	(mpa)->copyright  = MPA_MARSHAL_COPYRIGHT(n);  \
+	(mpa)->original   = MPA_MARSHAL_ORIGINAL(n);   \
+	(mpa)->emphasis   = MPA_MARSHAL_EMPHASIS(n);   \
+	} while (0)
+
+extern const int mpa_versions[4];
+extern const int mpa_layers[4];
+extern const unsigned mpa_samples[3][3];
+extern const unsigned mpa_bitrates[3][3][16]; /* kb/s */
+extern const unsigned mpa_frequencies[3][4]; /* Hz */
+extern const unsigned mpa_padding[3];
+
+#define MPA_VERSION(mpa)   (mpa_versions[(mpa)->version])
+#define MPA_LAYER(mpa)     (mpa_layers[(mpa)->layer])
+#define MPAV(mpa) MPA_VERSION(mpa)
+#define MPAL(mpa) MPA_LAYER(mpa)
+
+#define MPA_SAMPLES(mpa) (mpa_samples[MPAV(mpa)][MPAL(mpa)])
+#define MPA_BITRATE(mpa) (1000 * \
+	(mpa_bitrates[MPAV(mpa)][MPAL(mpa)][(mpa)->bitrate]))
+#define MPA_FREQUENCY(mpa) \
+	(mpa_frequencies[MPAV(mpa)][(mpa)->frequency])
+#define MPA_PADDING(mpa)((mpa)->padding ? mpa_padding[MPAL(mpa)] : 0)
+
+#define MPA_DATA_BYTES(mpa) (MPA_BITRATE(mpa) * MPA_SAMPLES(mpa) \
+		/ MPA_FREQUENCY(mpa) / 8)
+#define MPA_BYTES(mpa) (MPA_DATA_BYTES(mpa) + MPA_PADDING(mpa))
+#define MPA_DURATION_NS(mpa) \
+	(1000000000 / MPA_FREQUENCY(mpa) * MPA_SAMPLES(mpa))
+
+enum { MPA_SYNC = 0x7ff };
+
+#define MPA_SYNC_VALID(mpa)      ((mpa)->sync == MPA_SYNC)
+#define MPA_VERSION_VALID(mpa)   (MPA_VERSION(mpa) >= 0)
+#define MPA_LAYER_VALID(mpa)     (MPA_LAYER(mpa) >= 0)
+#define MPA_BITRATE_VALID(mpa)   (MPA_BITRATE(mpa) > 0)
+#define MPA_FREQUENCY_VALID(mpa) (MPA_FREQUENCY(mpa) > 0)
+#define MPA_VALID(mpa) (MPA_SYNC_VALID(mpa) \
+		&& MPA_VERSION_VALID(mpa) && MPA_LAYER_VALID(mpa) \
+		&& MPA_BITRATE_VALID(mpa) && MPA_FREQUENCY_VALID(mpa))
+
+#endif
Index: wiretap/file_access.c
===================================================================
--- wiretap/file_access.c	(revision 21033)
+++ wiretap/file_access.c	(working copy)
@@ -72,6 +72,7 @@
 #include "k12.h"
 #include "ber.h"
 #include "catapult_dct2000.h"
+#include "mpeg.h"
 
 /* The open_file_* routines should return:
  *
@@ -111,6 +112,7 @@
 	k12_open,
 	catapult_dct2000_open,
 	ber_open,
+	mpeg_open,
 	/* Files that don't have magic bytes at a fixed location,
 	 * but that instead require a heuristic of some sort to
 	 * identify them.  This includes the ASCII trace files that
@@ -541,6 +543,9 @@
 	{ "Wildpacket Ether/AiroPeek (V9)", "peek9", "*.tpc;*.apc;*.pkt;*.wpz", ".pkt", FALSE,
 	  NULL, NULL },
     
+	/* WTAP_FILE_MPEG */
+	{ "MPEG", "mpeg", "*.mpeg;*.mpg;*.mp3", ".mpeg", FALSE,
+	  NULL, NULL },
 };
 
 /* Name that should be somewhat descriptive. */
Index: wiretap/mpeg.c
===================================================================
--- wiretap/mpeg.c	(revision 0)
+++ wiretap/mpeg.c	(revision 0)
@@ -0,0 +1,255 @@
+/* MPEG file format decoder for the Wiretap library.
+ * Written by Shaun Jackman <sjackman@xxxxxxxxx>.
+ * Copyright 2007 Shaun Jackman
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mpeg.h"
+#include "mpeg-audio.h"
+
+#include "wtap-int.h"
+#include "buffer.h"
+#include "file_wrappers.h"
+#include <arpa/inet.h>
+#include <errno.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define PES_PREFIX 1
+#define PES_VALID(n) (((n) >> 8 & 0xffffff) == PES_PREFIX)
+
+static size_t mpeg_resync(wtap *wth, int *err, gchar **err_info)
+{
+	(void)err_info;
+	off_t offset = file_tell(wth->fh);
+	size_t count = 0;
+	int sync = file_getc(wth->fh);
+	while (sync != EOF) {
+		if (sync == 0xff && count > 0) {
+			sync = file_getc(wth->fh);
+			if (sync != EOF && (sync & 0xe0) == 0xe0)
+				break;
+		} else
+			sync = file_getc(wth->fh);
+		count++;
+	}
+	file_seek(wth->fh, offset, SEEK_SET, err);
+	return count;
+}
+
+static int mpeg_read_header(wtap *wth, int *err, gchar **err_info,
+		uint32_t *n)
+{
+	(void)err_info;
+	errno = WTAP_ERR_CANT_READ;
+	int bytes_read = file_read(n, 1, sizeof *n, wth->fh);
+	if (bytes_read != sizeof *n) {
+		*err = file_error(wth->fh);
+		if (*err == 0 && bytes_read != 0)
+			*err = WTAP_ERR_SHORT_READ;
+		return -1;
+	}
+	*n = ntohl(*n);
+	if (file_seek(wth->fh, -sizeof *n, SEEK_CUR, err) == -1)
+		return -1;
+	return bytes_read;
+}
+
+static gboolean
+mpeg_read_rec_data(FILE_T fh, guchar *pd, int length, int *err)
+{
+	int	bytes_read;
+
+	errno = WTAP_ERR_CANT_READ;
+	bytes_read = file_read(pd, 1, length, fh);
+
+	if (bytes_read != length) {
+		*err = file_error(fh);
+		if (*err == 0)
+			*err = WTAP_ERR_SHORT_READ;
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static struct wtap_nstime now;
+static double t0;
+
+static gboolean mpeg_read(wtap *wth, int *err, gchar **err_info,
+		gint64 *data_offset)
+{
+	uint32_t n;
+	int bytes_read = mpeg_read_header(wth, err, err_info, &n);
+	if (bytes_read == -1)
+		return FALSE;
+	unsigned packet_size;
+	struct wtap_nstime ts = now;
+	if (PES_VALID(n)) {
+		off_t offset = file_tell(wth->fh);
+		if (offset == -1)
+			return -1;
+		if (file_seek(wth->fh, 3, SEEK_CUR, err) == -1)
+			return FALSE;
+
+		uint8_t stream;
+		int bytes_read = file_read(&stream, 1, sizeof stream, wth->fh);
+		if (bytes_read != sizeof stream) {
+			*err = file_error(wth->fh);
+			return FALSE;
+		}
+
+		if (stream == 0xba) {
+			uint32_t pack1;
+			bytes_read = file_read(&pack1, 1, sizeof pack1, wth->fh);
+			if (bytes_read != sizeof pack1) {
+				*err = file_error(wth->fh);
+				if (*err == 0 && bytes_read != 0)
+					*err = WTAP_ERR_SHORT_READ;
+				return FALSE;
+			}
+			uint32_t pack0;
+			bytes_read = file_read(&pack0, 1, sizeof pack0, wth->fh);
+			if (bytes_read != sizeof pack0) {
+				*err = file_error(wth->fh);
+				if (*err == 0 && bytes_read != 0)
+					*err = WTAP_ERR_SHORT_READ;
+				return FALSE;
+			}
+			uint64_t pack = (uint64_t)ntohl(pack1) << 32 | ntohl(pack0);
+
+			switch (pack >> 62) {
+				case 1:
+					if (file_seek(wth->fh, 1, SEEK_CUR, err) == -1)
+						return FALSE;
+					uint8_t stuffing;
+					bytes_read = file_read(&stuffing,
+							1, sizeof stuffing, wth->fh);
+					if (bytes_read != sizeof stuffing) {
+						*err = file_error(wth->fh);
+						return FALSE;
+					}
+					stuffing &= 0x07;
+					packet_size = 14 + stuffing;
+
+					uint32_t scr =
+						(pack >> 59 & 0x0007) << 30 |
+						(pack >> 43 & 0x7fff) << 15 |
+						(pack >> 27 & 0x7fff) << 0;
+					uint16_t scr_ext = pack >> 17 & 0x1ff;
+					double t = t0 + scr / 90e3 + scr_ext / 27e6;
+					double secs;
+					now.nsecs = modf(t, &secs) * 1e9;
+					now.secs = secs;
+					ts = now;
+					break;
+				default:
+					packet_size = 12;
+			}
+		} else {
+			uint16_t length;
+			bytes_read = file_read(&length, 1, sizeof length, wth->fh);
+			if (bytes_read != sizeof length) {
+				*err = file_error(wth->fh);
+				if (*err == 0 && bytes_read != 0)
+					*err = WTAP_ERR_SHORT_READ;
+				return FALSE;
+			}
+			length = ntohs(length);
+			packet_size = 6 + length;
+		}
+
+		if (file_seek(wth->fh, offset, SEEK_SET, err) == -1)
+			return FALSE;
+	} else {
+		struct mpa mpa;
+		MPA_MARSHAL(&mpa, n);
+		if (MPA_VALID(&mpa)) {
+			packet_size = MPA_BYTES(&mpa);
+			now.nsecs += MPA_DURATION_NS(&mpa);
+			if (now.nsecs >= 1000000000) {
+				now.secs++;
+				now.nsecs -= 1000000000;
+			}
+		} else {
+			packet_size = mpeg_resync(wth, err, err_info);
+			if (packet_size == 0)
+				return FALSE;
+		}
+	}
+	*data_offset = wth->data_offset;
+
+	buffer_assure_space(wth->frame_buffer, packet_size);
+	if (!mpeg_read_rec_data(wth->fh, buffer_start_ptr(wth->frame_buffer),
+				packet_size, err))
+		return FALSE;
+	wth->data_offset += packet_size;
+	wth->phdr.ts = ts;
+	wth->phdr.caplen = packet_size;
+	wth->phdr.len = packet_size;
+	return TRUE;
+}
+
+static gboolean
+mpeg_seek_read(wtap *wth, gint64 seek_off,
+		union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
+		int *err, gchar **err_info)
+{
+	(void)pseudo_header; (void)err_info;
+	if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+		return FALSE;
+	return mpeg_read_rec_data(wth->random_fh, pd, length, err);
+}
+
+static void
+mpeg_close(wtap *wth)
+{
+	(void)wth;
+}
+
+int mpeg_open(wtap *wth, int *err, gchar **err_info)
+{
+	now.secs = time(NULL);
+	now.nsecs = 0;
+	t0 = now.secs;
+
+	uint32_t n;
+	if (mpeg_read_header(wth, err, err_info, &n) == -1)
+		return -1;
+	struct mpa mpa;
+	MPA_MARSHAL(&mpa, n);
+	if (!MPA_SYNC_VALID(&mpa)) {
+		off_t offset = file_tell(wth->fh);
+		if (offset == -1)
+			return -1;
+		size_t count = mpeg_resync(wth, err, err_info);
+		if (count == 0)
+			return 0;
+		if (file_seek(wth->fh, count, SEEK_CUR, err) == -1)
+			return -1;
+		if (mpeg_read_header(wth, err, err_info, &n) == -1)
+			return 0;
+		MPA_MARSHAL(&mpa, n);
+		if (!MPA_SYNC_VALID(&mpa))
+			return 0;
+		if (file_seek(wth->fh, offset, SEEK_SET, err) == -1)
+			return -1;
+	}
+
+	wth->file_type = WTAP_FILE_MPEG;
+	wth->file_encap = WTAP_ENCAP_MPEG;
+	wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
+	wth->subtype_read = mpeg_read;
+	wth->subtype_seek_read = mpeg_seek_read;
+	wth->subtype_close = mpeg_close;
+	wth->snapshot_length = 0;
+	return 1;
+}
Index: wiretap/mpeg.h
===================================================================
--- wiretap/mpeg.h	(revision 0)
+++ wiretap/mpeg.h	(revision 0)
@@ -0,0 +1,8 @@
+#ifndef __W_MPEG_H__
+#define __W_MPEG_H__
+
+#include "wtap-int.h"
+
+int mpeg_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
Index: wiretap/wtap.c
===================================================================
--- wiretap/wtap.c	(revision 21033)
+++ wiretap/wtap.c	(working copy)
@@ -371,7 +371,10 @@
 	{ "IEEE 802.16 MAC Common Part Sublayer", "ieee-802-16-mac-cps" },
 
 	/* WTAP_ENCAP_NETTL_RAW_TELNET */
-	{ "Raw telnet with nettl headers", "raw-telnet-nettl" }
+	{ "Raw telnet with nettl headers", "raw-telnet-nettl" },
+
+	/* WTAP_ENCAP_MPEG */
+	{ "MPEG", "mpeg" },
 };
 
 /* Name that should be somewhat descriptive. */
Index: wiretap/wtap.h
===================================================================
--- wiretap/wtap.h	(revision 21033)
+++ wiretap/wtap.h	(working copy)
@@ -190,9 +190,10 @@
 #define WTAP_ENCAP_IEEE802_16_MAC_CPS		93
 #define WTAP_ENCAP_NETTL_RAW_TELNET		94
 #define WTAP_ENCAP_USB_LINUX                    95
+#define WTAP_ENCAP_MPEG                         96
 
 /* last WTAP_ENCAP_ value + 1 */
-#define WTAP_NUM_ENCAP_TYPES			96
+#define WTAP_NUM_ENCAP_TYPES			97
 
 /* File types that can be read by wiretap.
    We support writing some many of these file types, too, so we
@@ -243,8 +244,9 @@
 #define WTAP_FILE_ETHERPEEK_V56			43
 #define WTAP_FILE_ETHERPEEK_V7			44
 #define WTAP_FILE_AIROPEEK_V9			45
+#define WTAP_FILE_MPEG				46
 
-#define WTAP_NUM_FILE_TYPES			46
+#define WTAP_NUM_FILE_TYPES			47
 
 /* timestamp precision (currently only these values are supported) */
 #define WTAP_FILE_TSPREC_SEC		0
Index: wiretap/Makefile.common
===================================================================
--- wiretap/Makefile.common	(revision 21033)
+++ wiretap/Makefile.common	(working copy)
@@ -51,6 +51,8 @@
 	k12.c			\
 	lanalyzer.c		\
 	libpcap.c		\
+	mpeg.c			\
+	mpeg-audio.c		\
 	netmon.c		\
 	nettl.c			\
 	network_instruments.c	\
@@ -90,6 +92,8 @@
 	k12.h			\
 	lanalyzer.h		\
 	libpcap.h		\
+	mpeg.h			\
+	mpeg-audio.h		\
 	netmon.h		\
 	nettl.h			\
 	network_instruments.h	\