#include <stdlib.h> not needed;
[obnox/wireshark/wip.git] / wiretap / mpeg.c
1 /* mpeg.c
2  *
3  * MPEG file format decoder for the Wiretap library.
4  * Written by Shaun Jackman <sjackman@gmail.com>
5  * Copyright 2007 Shaun Jackman
6  *
7  * $Id$
8  *
9  * Wiretap Library
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #include "mpeg.h"
38 #include "wsutil/mpeg-audio.h"
39
40 #include "wtap-int.h"
41 #include "buffer.h"
42 #include "file_wrappers.h"
43 #include <errno.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <time.h>
47
48 #define PES_PREFIX 1
49 #define PES_VALID(n) (((n) >> 8 & 0xffffff) == PES_PREFIX)
50
51 typedef struct {
52         struct wtap_nstime now;
53         time_t t0;
54 } mpeg_t;
55
56 static int 
57 mpeg_resync(wtap *wth, int *err, gchar **err_info _U_)
58 {
59         gint64 offset = file_tell(wth->fh);
60         int count = 0;
61         int byte = file_getc(wth->fh);
62
63         while (byte != EOF) {
64                 if (byte == 0xff && count > 0) {
65                         byte = file_getc(wth->fh);
66                         if (byte != EOF && (byte & 0xe0) == 0xe0)
67                                 break;
68                 } else
69                         byte = file_getc(wth->fh);
70                 count++;
71         }
72         if (file_seek(wth->fh, offset, SEEK_SET, err) == -1)
73                 return 0;
74         return count;
75 }
76
77 static int 
78 mpeg_read_header(wtap *wth, int *err, gchar **err_info, guint32 *n)
79 {
80         int bytes_read;
81
82         errno = WTAP_ERR_CANT_READ;
83         bytes_read = file_read(n, sizeof *n, wth->fh);
84         if (bytes_read != sizeof *n) {
85                 *err = file_error(wth->fh, err_info);
86                 if (*err == 0 && bytes_read != 0)
87                         *err = WTAP_ERR_SHORT_READ;
88                 return -1;
89         }
90         *n = g_ntohl(*n);
91         if (file_seek(wth->fh, -(gint64)(sizeof *n), SEEK_CUR, err) == -1)
92                 return -1;
93         return bytes_read;
94 }
95
96 static gboolean
97 mpeg_read_rec_data(FILE_T fh, guchar *pd, int length, int *err,
98                 gchar **err_info)
99 {
100         int     bytes_read;
101
102         errno = WTAP_ERR_CANT_READ;
103         bytes_read = file_read(pd, length, fh);
104
105         if (bytes_read != length) {
106                 *err = file_error(fh, err_info);
107                 if (*err == 0)
108                         *err = WTAP_ERR_SHORT_READ;
109                 return FALSE;
110         }
111         return TRUE;
112 }
113
114 #define SCRHZ 27000000
115
116 static gboolean 
117 mpeg_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
118 {
119         mpeg_t *mpeg = (mpeg_t *)wth->priv;
120         guint32 n;
121         int bytes_read = mpeg_read_header(wth, err, err_info, &n);
122         unsigned int packet_size;
123         struct wtap_nstime ts = mpeg->now;
124
125         if (bytes_read == -1)
126                 return FALSE;
127         if (PES_VALID(n)) {
128                 gint64 offset = file_tell(wth->fh);
129                 guint8 stream;
130
131                 if (offset == -1)
132                         return -1;
133                 if (file_seek(wth->fh, 3, SEEK_CUR, err) == -1)
134                         return FALSE;
135
136                 bytes_read = file_read(&stream, sizeof stream, wth->fh);
137                 if (bytes_read != sizeof stream) {
138                         *err = file_error(wth->fh, err_info);
139                         return FALSE;
140                 }
141
142                 if (stream == 0xba) {
143                         guint32 pack1;
144                         guint32 pack0;
145                         guint64 pack;
146                         guint8 stuffing;
147
148                         bytes_read = file_read(&pack1, sizeof pack1, wth->fh);
149                         if (bytes_read != sizeof pack1) {
150                                 *err = file_error(wth->fh, err_info);
151                                 if (*err == 0 && bytes_read != 0)
152                                         *err = WTAP_ERR_SHORT_READ;
153                                 return FALSE;
154                         }
155                         bytes_read = file_read(&pack0, sizeof pack0, wth->fh);
156                         if (bytes_read != sizeof pack0) {
157                                 *err = file_error(wth->fh, err_info);
158                                 if (*err == 0 && bytes_read != 0)
159                                         *err = WTAP_ERR_SHORT_READ;
160                                 return FALSE;
161                         }
162                         pack = (guint64)g_ntohl(pack1) << 32 | g_ntohl(pack0);
163
164                         switch (pack >> 62) {
165                                 case 1:
166                                         if (file_seek(wth->fh, 1, SEEK_CUR, err) == -1)
167                                                 return FALSE;
168                                         bytes_read = file_read(&stuffing,
169                                                         sizeof stuffing, wth->fh);
170                                         if (bytes_read != sizeof stuffing) {
171                                                 *err = file_error(wth->fh, err_info);
172                                                 return FALSE;
173                                         }
174                                         stuffing &= 0x07;
175                                         packet_size = 14 + stuffing;
176
177                                         {
178                                                 guint64 bytes = pack >> 16;
179                                                 guint64 ts_val =
180                                                         (bytes >> 43 & 0x0007) << 30 |
181                                                         (bytes >> 27 & 0x7fff) << 15 |
182                                                         (bytes >> 11 & 0x7fff) << 0;
183                                                 unsigned ext = (unsigned)((bytes >> 1) & 0x1ff);
184                                                 guint64 cr = 300 * ts_val + ext;
185                                                 unsigned rem = (unsigned)(cr % SCRHZ);
186                                                 mpeg->now.secs
187                                                         = mpeg->t0 + (time_t)(cr / SCRHZ);
188                                                 mpeg->now.nsecs
189                                                         = (int)(G_GINT64_CONSTANT(1000000000) * rem / SCRHZ);
190                                         }
191                                         ts = mpeg->now;
192                                         break;
193                                 default:
194                                         packet_size = 12;
195                         }
196                 } else {
197                         guint16 length;
198                         bytes_read = file_read(&length, sizeof length, wth->fh);
199                         if (bytes_read != sizeof length) {
200                                 *err = file_error(wth->fh, err_info);
201                                 if (*err == 0 && bytes_read != 0)
202                                         *err = WTAP_ERR_SHORT_READ;
203                                 return FALSE;
204                         }
205                         length = g_ntohs(length);
206                         packet_size = 6 + length;
207                 }
208
209                 if (file_seek(wth->fh, offset, SEEK_SET, err) == -1)
210                         return FALSE;
211         } else {
212                 struct mpa mpa;
213
214                 MPA_UNMARSHAL(&mpa, n);
215                 if (MPA_VALID(&mpa)) {
216                         packet_size = MPA_BYTES(&mpa);
217                         mpeg->now.nsecs += MPA_DURATION_NS(&mpa);
218                         if (mpeg->now.nsecs >= 1000000000) {
219                                 mpeg->now.secs++;
220                                 mpeg->now.nsecs -= 1000000000;
221                         }
222                 } else {
223                         packet_size = mpeg_resync(wth, err, err_info);
224                         if (packet_size == 0)
225                                 return FALSE;
226                 }
227         }
228         *data_offset = wth->data_offset;
229
230         buffer_assure_space(wth->frame_buffer, packet_size);
231         if (!mpeg_read_rec_data(wth->fh, buffer_start_ptr(wth->frame_buffer),
232                                 packet_size, err, err_info))
233                 return FALSE;
234         wth->data_offset += packet_size;
235         wth->phdr.ts = ts;
236         wth->phdr.caplen = packet_size;
237         wth->phdr.len = packet_size;
238         return TRUE;
239 }
240
241 static gboolean
242 mpeg_seek_read(wtap *wth, gint64 seek_off,
243                 union wtap_pseudo_header *pseudo_header _U_, guchar *pd, int length,
244                 int *err, gchar **err_info)
245 {
246         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
247                 return FALSE;
248         return mpeg_read_rec_data(wth->random_fh, pd, length, err, err_info);
249 }
250
251 struct _mpeg_magic {
252         size_t len;
253         const gchar* match;
254 } magic[] = {
255         { 3, "TAG" }, /* ID3v1 */
256         { 3, "ID3" }, /* ID3v2 */
257         { 3, "\0\0\1" }, /* MPEG PES */
258         { 2, "\xff\xfb" }, /* MP3, taken from http://en.wikipedia.org/wiki/MP3#File_structure */
259         { 0, NULL }
260 };
261
262 int 
263 mpeg_open(wtap *wth, int *err, gchar **err_info)
264 {
265         int bytes_read;
266         char magic_buf[16];
267         struct _mpeg_magic* m;
268         mpeg_t *mpeg;
269         
270         errno = WTAP_ERR_CANT_READ;
271         bytes_read = file_read(magic_buf, sizeof magic_buf, wth->fh);
272         if (bytes_read != (int) sizeof magic_buf) {
273                 *err = file_error(wth->fh, err_info);
274                 if (*err != 0)
275                         return -1;
276                 return 0;
277         }
278
279         for (m=magic; m->match; m++) {
280                 if (memcmp(magic_buf, m->match, m->len) == 0)
281                         goto good_magic;
282         }
283         
284         return 0;
285
286 good_magic:
287         /* This appears to be a file with MPEG data. */
288         if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
289                 return -1;
290
291         wth->file_type = WTAP_FILE_MPEG;
292         wth->file_encap = WTAP_ENCAP_MPEG;
293         wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
294         wth->subtype_read = mpeg_read;
295         wth->subtype_seek_read = mpeg_seek_read;
296         wth->snapshot_length = 0;
297
298         mpeg = (mpeg_t *)g_malloc(sizeof(mpeg_t));
299         wth->priv = (void *)mpeg;
300         mpeg->now.secs = time(NULL);
301         mpeg->now.nsecs = 0;
302         mpeg->t0 = mpeg->now.secs;
303
304         return 1;
305 }