From Harald Welte:
[obnox/wireshark/wip.git] / wiretap / mpeg.c
index b62281a7a6be1c36257832f64414f7bff032d34f..cf60fe2375a8ce60d56f1dc570f61705749bc036 100644 (file)
 #endif
 
 #include "mpeg.h"
-#include "mpeg-audio.h"
+#include "wsutil/mpeg-audio.h"
 
 #include "wtap-int.h"
 #include "buffer.h"
 #include "file_wrappers.h"
 #include <errno.h>
-#include <math.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 
+typedef struct {
+       struct wtap_nstime now;
+       time_t t0;
+} mpeg_t;
+
+static int 
 mpeg_resync(wtap *wth, int *err, gchar **err_info _U_)
 {
        gint64 offset = file_tell(wth->fh);
-       size_t count = 0;
-       int sync = file_getc(wth->fh);
+       int count = 0;
+       int byte = file_getc(wth->fh);
 
-       while (sync != EOF) {
-               if (sync == 0xff && count > 0) {
-                       sync = file_getc(wth->fh);
-                       if (sync != EOF && (sync & 0xe0) == 0xe0)
+       while (byte != EOF) {
+               if (byte == 0xff && count > 0) {
+                       byte = file_getc(wth->fh);
+                       if (byte != EOF && (byte & 0xe0) == 0xe0)
                                break;
                } else
-                       sync = file_getc(wth->fh);
+                       byte = file_getc(wth->fh);
                count++;
        }
-       file_seek(wth->fh, offset, SEEK_SET, err);
+       if (file_seek(wth->fh, offset, SEEK_SET, err) == -1)
+               return 0;
        return count;
 }
 
 static int 
-mpeg_read_header(wtap *wth, int *err, gchar **err_info _U_,
-               guint32 *n)
+mpeg_read_header(wtap *wth, int *err, gchar **err_info, guint32 *n)
 {
        int bytes_read;
 
        errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(n, 1, sizeof *n, wth->fh);
+       bytes_read = file_read(n, sizeof *n, wth->fh);
        if (bytes_read != sizeof *n) {
-               *err = file_error(wth->fh);
+               *err = file_error(wth->fh, err_info);
                if (*err == 0 && bytes_read != 0)
                        *err = WTAP_ERR_SHORT_READ;
                return -1;
@@ -90,15 +94,16 @@ mpeg_read_header(wtap *wth, int *err, gchar **err_info _U_,
 }
 
 static gboolean
-mpeg_read_rec_data(FILE_T fh, guchar *pd, int length, int *err)
+mpeg_read_rec_data(FILE_T fh, guint8 *pd, int length, int *err,
+               gchar **err_info)
 {
        int     bytes_read;
 
        errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(pd, 1, length, fh);
+       bytes_read = file_read(pd, length, fh);
 
        if (bytes_read != length) {
-               *err = file_error(fh);
+               *err = file_error(fh, err_info);
                if (*err == 0)
                        *err = WTAP_ERR_SHORT_READ;
                return FALSE;
@@ -106,33 +111,31 @@ mpeg_read_rec_data(FILE_T fh, guchar *pd, int length, int *err)
        return TRUE;
 }
 
-static struct wtap_nstime now;
-static double t0;
+#define SCRHZ 27000000
 
 static gboolean 
-mpeg_read(wtap *wth, int *err, gchar **err_info _U_,
-               gint64 *data_offset)
+mpeg_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
 {
+       mpeg_t *mpeg = (mpeg_t *)wth->priv;
        guint32 n;
        int bytes_read = mpeg_read_header(wth, err, err_info, &n);
-       unsigned packet_size;
-       struct wtap_nstime ts = now;
+       unsigned int packet_size;
+       struct wtap_nstime ts = mpeg->now;
 
        if (bytes_read == -1)
                return FALSE;
        if (PES_VALID(n)) {
                gint64 offset = file_tell(wth->fh);
                guint8 stream;
-               int bytes_read;
 
                if (offset == -1)
                        return -1;
                if (file_seek(wth->fh, 3, SEEK_CUR, err) == -1)
                        return FALSE;
 
-               bytes_read = file_read(&stream, 1, sizeof stream, wth->fh);
+               bytes_read = file_read(&stream, sizeof stream, wth->fh);
                if (bytes_read != sizeof stream) {
-                       *err = file_error(wth->fh);
+                       *err = file_error(wth->fh, err_info);
                        return FALSE;
                }
 
@@ -141,21 +144,17 @@ mpeg_read(wtap *wth, int *err, gchar **err_info _U_,
                        guint32 pack0;
                        guint64 pack;
                        guint8 stuffing;
-                       guint32 scr;
-                       guint16 scr_ext;
-                       double t;
-                       double secs;
 
-                       bytes_read = file_read(&pack1, 1, sizeof pack1, wth->fh);
+                       bytes_read = file_read(&pack1, sizeof pack1, wth->fh);
                        if (bytes_read != sizeof pack1) {
-                               *err = file_error(wth->fh);
+                               *err = file_error(wth->fh, err_info);
                                if (*err == 0 && bytes_read != 0)
                                        *err = WTAP_ERR_SHORT_READ;
                                return FALSE;
                        }
-                       bytes_read = file_read(&pack0, 1, sizeof pack0, wth->fh);
+                       bytes_read = file_read(&pack0, sizeof pack0, wth->fh);
                        if (bytes_read != sizeof pack0) {
-                               *err = file_error(wth->fh);
+                               *err = file_error(wth->fh, err_info);
                                if (*err == 0 && bytes_read != 0)
                                        *err = WTAP_ERR_SHORT_READ;
                                return FALSE;
@@ -167,33 +166,38 @@ mpeg_read(wtap *wth, int *err, gchar **err_info _U_,
                                        if (file_seek(wth->fh, 1, SEEK_CUR, err) == -1)
                                                return FALSE;
                                        bytes_read = file_read(&stuffing,
-                                                       1, sizeof stuffing, wth->fh);
+                                                       sizeof stuffing, wth->fh);
                                        if (bytes_read != sizeof stuffing) {
-                                               *err = file_error(wth->fh);
+                                               *err = file_error(wth->fh, err_info);
                                                return FALSE;
                                        }
                                        stuffing &= 0x07;
                                        packet_size = 14 + stuffing;
 
-                                       scr = (guint32)
-                                               ((pack >> 59 & 0x0007) << 30 |
-                                               (pack >> 43 & 0x7fff) << 15 |
-                                                (pack >> 27 & 0x7fff) << 0);
-                                       scr_ext = (guint16)(pack >> 17 & 0x1ff);
-                                       t = t0 + scr / 90e3 + scr_ext / 27e6;
-
-                                       now.nsecs = (int)(modf(t, &secs) * 1e9);
-                                       now.secs = (time_t)secs;
-                                       ts = now;
+                                       {
+                                               guint64 bytes = pack >> 16;
+                                               guint64 ts_val =
+                                                       (bytes >> 43 & 0x0007) << 30 |
+                                                       (bytes >> 27 & 0x7fff) << 15 |
+                                                       (bytes >> 11 & 0x7fff) << 0;
+                                               unsigned ext = (unsigned)((bytes >> 1) & 0x1ff);
+                                               guint64 cr = 300 * ts_val + ext;
+                                               unsigned rem = (unsigned)(cr % SCRHZ);
+                                               mpeg->now.secs
+                                                       = mpeg->t0 + (time_t)(cr / SCRHZ);
+                                               mpeg->now.nsecs
+                                                       = (int)(G_GINT64_CONSTANT(1000000000) * rem / SCRHZ);
+                                       }
+                                       ts = mpeg->now;
                                        break;
                                default:
                                        packet_size = 12;
                        }
                } else {
                        guint16 length;
-                       bytes_read = file_read(&length, 1, sizeof length, wth->fh);
+                       bytes_read = file_read(&length, sizeof length, wth->fh);
                        if (bytes_read != sizeof length) {
-                               *err = file_error(wth->fh);
+                               *err = file_error(wth->fh, err_info);
                                if (*err == 0 && bytes_read != 0)
                                        *err = WTAP_ERR_SHORT_READ;
                                return FALSE;
@@ -210,10 +214,10 @@ mpeg_read(wtap *wth, int *err, gchar **err_info _U_,
                MPA_UNMARSHAL(&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;
+                       mpeg->now.nsecs += MPA_DURATION_NS(&mpa);
+                       if (mpeg->now.nsecs >= 1000000000) {
+                               mpeg->now.secs++;
+                               mpeg->now.nsecs -= 1000000000;
                        }
                } else {
                        packet_size = mpeg_resync(wth, err, err_info);
@@ -225,7 +229,7 @@ mpeg_read(wtap *wth, int *err, gchar **err_info _U_,
 
        buffer_assure_space(wth->frame_buffer, packet_size);
        if (!mpeg_read_rec_data(wth->fh, buffer_start_ptr(wth->frame_buffer),
-                               packet_size, err))
+                               packet_size, err, err_info))
                return FALSE;
        wth->data_offset += packet_size;
        wth->phdr.ts = ts;
@@ -236,87 +240,66 @@ mpeg_read(wtap *wth, int *err, gchar **err_info _U_,
 
 static gboolean
 mpeg_seek_read(wtap *wth, gint64 seek_off,
-               union wtap_pseudo_header *pseudo_header _U_, guchar *pd, int length,
-               int *err, gchar **err_info _U_)
+               union wtap_pseudo_header *pseudo_header _U_, guint8 *pd, int length,
+               int *err, gchar **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 _U_)
-{
-
+       return mpeg_read_rec_data(wth->random_fh, pd, length, err, err_info);
 }
 
-/* XXX We probably need more magic to open more types */
 struct _mpeg_magic {
        size_t len;
-       gchar* match;
+       const gchar* match;
 } magic[] = {
-       {3,"ID3"},
-       {0,NULL}
+       { 3, "TAG" }, /* ID3v1 */
+       { 3, "ID3" }, /* ID3v2 */
+       { 3, "\0\0\1" }, /* MPEG PES */
+       { 2, "\xff\xfb" }, /* MP3, taken from http://en.wikipedia.org/wiki/MP3#File_structure */
+       { 0, NULL }
 };
 
 int 
 mpeg_open(wtap *wth, int *err, gchar **err_info)
 {
-       guint32 n;
-       struct mpa mpa;
-       struct _mpeg_magic* m;
+       int bytes_read;
        char magic_buf[16];
+       struct _mpeg_magic* m;
+       mpeg_t *mpeg;
        
-       now.secs = time(NULL);
-       now.nsecs = 0;
-       t0 = (double) now.secs;
-
-       if ( file_read(magic_buf, 1, 16, wth->fh) != 16 ) {
-               return -1;
-       } else {
-               if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+       errno = WTAP_ERR_CANT_READ;
+       bytes_read = file_read(magic_buf, sizeof magic_buf, wth->fh);
+       if (bytes_read != (int) sizeof magic_buf) {
+               *err = file_error(wth->fh, err_info);
+               if (*err != 0)
                        return -1;
+               return 0;
        }
 
-       for (m=magic;m->match;m++) {
-               if (memcmp(magic_buf,m->match,m->len) == 0)
+       for (m=magic; m->match; m++) {
+               if (memcmp(magic_buf, m->match, m->len) == 0)
                        goto good_magic;
        }
        
-       return -1;
-       
+       return 0;
+
 good_magic:
-       if (mpeg_read_header(wth, err, err_info, &n) == -1)
+       /* This appears to be a file with MPEG data. */
+       if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
                return -1;
-       
-       MPA_UNMARSHAL(&mpa, n);
-       if (!MPA_SYNC_VALID(&mpa)) {
-               gint64 offset;
-               size_t count;
-
-               offset = file_tell(wth->fh);
-               if (offset == -1)
-                       return -1;
-               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_UNMARSHAL(&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;
+
+       mpeg = (mpeg_t *)g_malloc(sizeof(mpeg_t));
+       wth->priv = (void *)mpeg;
+       mpeg->now.secs = time(NULL);
+       mpeg->now.nsecs = 0;
+       mpeg->t0 = mpeg->now.secs;
+
        return 1;
 }