Second step in introducing asn context to BER dissectors just like in PER.
[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 "mpeg-audio.h"
39
40 #include "wtap-int.h"
41 #include "buffer.h"
42 #include "file_wrappers.h"
43 #include <errno.h>
44 #include <math.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <time.h>
48
49 #define PES_PREFIX 1
50 #define PES_VALID(n) (((n) >> 8 & 0xffffff) == PES_PREFIX)
51
52 static size_t 
53 mpeg_resync(wtap *wth, int *err, gchar **err_info _U_)
54 {
55         gint64 offset = file_tell(wth->fh);
56         size_t count = 0;
57         int sync = file_getc(wth->fh);
58
59         while (sync != EOF) {
60                 if (sync == 0xff && count > 0) {
61                         sync = file_getc(wth->fh);
62                         if (sync != EOF && (sync & 0xe0) == 0xe0)
63                                 break;
64                 } else
65                         sync = file_getc(wth->fh);
66                 count++;
67         }
68         file_seek(wth->fh, offset, SEEK_SET, err);
69         return count;
70 }
71
72 static int 
73 mpeg_read_header(wtap *wth, int *err, gchar **err_info _U_,
74                 guint32 *n)
75 {
76         int bytes_read;
77
78         errno = WTAP_ERR_CANT_READ;
79         bytes_read = file_read(n, 1, sizeof *n, wth->fh);
80         if (bytes_read != sizeof *n) {
81                 *err = file_error(wth->fh);
82                 if (*err == 0 && bytes_read != 0)
83                         *err = WTAP_ERR_SHORT_READ;
84                 return -1;
85         }
86         *n = g_ntohl(*n);
87         if (file_seek(wth->fh, -(gint64)(sizeof *n), SEEK_CUR, err) == -1)
88                 return -1;
89         return bytes_read;
90 }
91
92 static gboolean
93 mpeg_read_rec_data(FILE_T fh, guchar *pd, int length, int *err)
94 {
95         int     bytes_read;
96
97         errno = WTAP_ERR_CANT_READ;
98         bytes_read = file_read(pd, 1, length, fh);
99
100         if (bytes_read != length) {
101                 *err = file_error(fh);
102                 if (*err == 0)
103                         *err = WTAP_ERR_SHORT_READ;
104                 return FALSE;
105         }
106         return TRUE;
107 }
108
109 static gboolean 
110 mpeg_read(wtap *wth, int *err, gchar **err_info _U_,
111                 gint64 *data_offset)
112 {
113         guint32 n;
114         int bytes_read = mpeg_read_header(wth, err, err_info, &n);
115         unsigned packet_size;
116         struct wtap_nstime ts = wth->capture.mpeg->now;
117
118         if (bytes_read == -1)
119                 return FALSE;
120         if (PES_VALID(n)) {
121                 gint64 offset = file_tell(wth->fh);
122                 guint8 stream;
123                 int bytes_read;
124
125                 if (offset == -1)
126                         return -1;
127                 if (file_seek(wth->fh, 3, SEEK_CUR, err) == -1)
128                         return FALSE;
129
130                 bytes_read = file_read(&stream, 1, sizeof stream, wth->fh);
131                 if (bytes_read != sizeof stream) {
132                         *err = file_error(wth->fh);
133                         return FALSE;
134                 }
135
136                 if (stream == 0xba) {
137                         guint32 pack1;
138                         guint32 pack0;
139                         guint64 pack;
140                         guint8 stuffing;
141                         guint32 scr;
142                         guint16 scr_ext;
143                         double t;
144                         double secs;
145
146                         bytes_read = file_read(&pack1, 1, sizeof pack1, wth->fh);
147                         if (bytes_read != sizeof pack1) {
148                                 *err = file_error(wth->fh);
149                                 if (*err == 0 && bytes_read != 0)
150                                         *err = WTAP_ERR_SHORT_READ;
151                                 return FALSE;
152                         }
153                         bytes_read = file_read(&pack0, 1, sizeof pack0, wth->fh);
154                         if (bytes_read != sizeof pack0) {
155                                 *err = file_error(wth->fh);
156                                 if (*err == 0 && bytes_read != 0)
157                                         *err = WTAP_ERR_SHORT_READ;
158                                 return FALSE;
159                         }
160                         pack = (guint64)g_ntohl(pack1) << 32 | g_ntohl(pack0);
161
162                         switch (pack >> 62) {
163                                 case 1:
164                                         if (file_seek(wth->fh, 1, SEEK_CUR, err) == -1)
165                                                 return FALSE;
166                                         bytes_read = file_read(&stuffing,
167                                                         1, sizeof stuffing, wth->fh);
168                                         if (bytes_read != sizeof stuffing) {
169                                                 *err = file_error(wth->fh);
170                                                 return FALSE;
171                                         }
172                                         stuffing &= 0x07;
173                                         packet_size = 14 + stuffing;
174
175                                         scr = (guint32)
176                                                 ((pack >> 59 & 0x0007) << 30 |
177                                                 (pack >> 43 & 0x7fff) << 15 |
178                                                  (pack >> 27 & 0x7fff) << 0);
179                                         scr_ext = (guint16)(pack >> 17 & 0x1ff);
180                                         t = wth->capture.mpeg->t0 + scr / 90e3 + scr_ext / 27e6;
181
182                                         wth->capture.mpeg->now.nsecs = (int)(modf(t, &secs) * 1e9);
183                                         wth->capture.mpeg->now.secs = (time_t)secs;
184                                         ts = wth->capture.mpeg->now;
185                                         break;
186                                 default:
187                                         packet_size = 12;
188                         }
189                 } else {
190                         guint16 length;
191                         bytes_read = file_read(&length, 1, sizeof length, wth->fh);
192                         if (bytes_read != sizeof length) {
193                                 *err = file_error(wth->fh);
194                                 if (*err == 0 && bytes_read != 0)
195                                         *err = WTAP_ERR_SHORT_READ;
196                                 return FALSE;
197                         }
198                         length = g_ntohs(length);
199                         packet_size = 6 + length;
200                 }
201
202                 if (file_seek(wth->fh, offset, SEEK_SET, err) == -1)
203                         return FALSE;
204         } else {
205                 struct mpa mpa;
206
207                 MPA_UNMARSHAL(&mpa, n);
208                 if (MPA_VALID(&mpa)) {
209                         packet_size = MPA_BYTES(&mpa);
210                         wth->capture.mpeg->now.nsecs += MPA_DURATION_NS(&mpa);
211                         if (wth->capture.mpeg->now.nsecs >= 1000000000) {
212                                 wth->capture.mpeg->now.secs++;
213                                 wth->capture.mpeg->now.nsecs -= 1000000000;
214                         }
215                 } else {
216                         packet_size = mpeg_resync(wth, err, err_info);
217                         if (packet_size == 0)
218                                 return FALSE;
219                 }
220         }
221         *data_offset = wth->data_offset;
222
223         buffer_assure_space(wth->frame_buffer, packet_size);
224         if (!mpeg_read_rec_data(wth->fh, buffer_start_ptr(wth->frame_buffer),
225                                 packet_size, err))
226                 return FALSE;
227         wth->data_offset += packet_size;
228         wth->phdr.ts = ts;
229         wth->phdr.caplen = packet_size;
230         wth->phdr.len = packet_size;
231         return TRUE;
232 }
233
234 static gboolean
235 mpeg_seek_read(wtap *wth, gint64 seek_off,
236                 union wtap_pseudo_header *pseudo_header _U_, guchar *pd, int length,
237                 int *err, gchar **err_info _U_)
238 {
239         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
240                 return FALSE;
241         return mpeg_read_rec_data(wth->random_fh, pd, length, err);
242 }
243
244 static void
245 mpeg_close(wtap *wth)
246 {
247         g_free(wth->capture.mpeg);
248 }
249
250 /* XXX We probably need more magic to open more types */
251 struct _mpeg_magic {
252         size_t len;
253         const gchar* match;
254 } magic[] = {
255         {3,"TAG"},
256         {3,"ID3"},
257         {0,NULL}
258 };
259
260 int 
261 mpeg_open(wtap *wth, int *err, gchar **err_info _U_)
262 {
263         int bytes_read;
264         char magic_buf[16];
265         struct _mpeg_magic* m;
266         
267         errno = WTAP_ERR_CANT_READ;
268         bytes_read = file_read(magic_buf, 1, sizeof magic_buf, wth->fh);
269         if (bytes_read != (int) sizeof magic_buf) {
270                 *err = file_error(wth->fh);
271                 if (*err != 0)
272                         return -1;
273                 return 0;
274         }
275
276         for (m=magic;m->match;m++) {
277                 if (memcmp(magic_buf,m->match,m->len) == 0)
278                         goto good_magic;
279         }
280         
281         return 0;
282
283 good_magic:
284         /* This appears to be a file with MPEG data. */
285         if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
286                 return -1;
287
288         wth->file_type = WTAP_FILE_MPEG;
289         wth->file_encap = WTAP_ENCAP_MPEG;
290         wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
291         wth->subtype_read = mpeg_read;
292         wth->subtype_seek_read = mpeg_seek_read;
293         wth->subtype_close = mpeg_close;
294         wth->snapshot_length = 0;
295
296         wth->capture.mpeg = g_malloc(sizeof(libpcap_t));
297         wth->capture.mpeg->now.secs = time(NULL);
298         wth->capture.mpeg->now.nsecs = 0;
299         wth->capture.mpeg->t0 = (double) wth->capture.mpeg->now.secs;
300
301         return 1;
302 }