Replace wtap_nstime with nstime_t, remove wtap_nstime_to_sec.
[metze/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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #ifdef HAVE_SYS_TYPES_H
28 #include <sys/types.h>
29 #endif
30
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34
35 #include "mpeg.h"
36 #include "wsutil/mpeg-audio.h"
37
38 #include "wtap-int.h"
39 #include "buffer.h"
40 #include "file_wrappers.h"
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45
46 #define PES_PREFIX 1
47 #define PES_VALID(n) (((n) >> 8 & 0xffffff) == PES_PREFIX)
48
49 typedef struct {
50         nstime_t now;
51         time_t t0;
52 } mpeg_t;
53
54 static int
55 mpeg_resync(wtap *wth, int *err, gchar **err_info _U_)
56 {
57         gint64 offset = file_tell(wth->fh);
58         int count = 0;
59         int byte = file_getc(wth->fh);
60
61         while (byte != EOF) {
62                 if (byte == 0xff && count > 0) {
63                         byte = file_getc(wth->fh);
64                         if (byte != EOF && (byte & 0xe0) == 0xe0)
65                                 break;
66                 } else
67                         byte = file_getc(wth->fh);
68                 count++;
69         }
70         if (file_seek(wth->fh, offset, SEEK_SET, err) == -1)
71                 return 0;
72         return count;
73 }
74
75 static int
76 mpeg_read_header(wtap *wth, int *err, gchar **err_info, guint32 *n)
77 {
78         int bytes_read;
79
80         errno = WTAP_ERR_CANT_READ;
81         bytes_read = file_read(n, sizeof *n, wth->fh);
82         if (bytes_read != sizeof *n) {
83                 *err = file_error(wth->fh, err_info);
84                 if (*err == 0 && bytes_read != 0)
85                         *err = WTAP_ERR_SHORT_READ;
86                 return -1;
87         }
88         *n = g_ntohl(*n);
89         if (file_seek(wth->fh, -(gint64)(sizeof *n), SEEK_CUR, err) == -1)
90                 return -1;
91         return bytes_read;
92 }
93
94 #define SCRHZ 27000000
95
96 static gboolean
97 mpeg_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
98 {
99         mpeg_t *mpeg = (mpeg_t *)wth->priv;
100         guint32 n;
101         int bytes_read = mpeg_read_header(wth, err, err_info, &n);
102         unsigned int packet_size;
103         nstime_t ts = mpeg->now;
104
105         if (bytes_read == -1)
106                 return FALSE;
107         if (PES_VALID(n)) {
108                 gint64 offset = file_tell(wth->fh);
109                 guint8 stream;
110
111                 if (offset == -1)
112                         return -1;
113                 if (file_seek(wth->fh, 3, SEEK_CUR, err) == -1)
114                         return FALSE;
115
116                 bytes_read = file_read(&stream, sizeof stream, wth->fh);
117                 if (bytes_read != sizeof stream) {
118                         *err = file_error(wth->fh, err_info);
119                         return FALSE;
120                 }
121
122                 if (stream == 0xba) {
123                         guint32 pack1;
124                         guint32 pack0;
125                         guint64 pack;
126                         guint8 stuffing;
127
128                         bytes_read = file_read(&pack1, sizeof pack1, wth->fh);
129                         if (bytes_read != sizeof pack1) {
130                                 *err = file_error(wth->fh, err_info);
131                                 if (*err == 0 && bytes_read != 0)
132                                         *err = WTAP_ERR_SHORT_READ;
133                                 return FALSE;
134                         }
135                         bytes_read = file_read(&pack0, sizeof pack0, wth->fh);
136                         if (bytes_read != sizeof pack0) {
137                                 *err = file_error(wth->fh, err_info);
138                                 if (*err == 0 && bytes_read != 0)
139                                         *err = WTAP_ERR_SHORT_READ;
140                                 return FALSE;
141                         }
142                         pack = (guint64)g_ntohl(pack1) << 32 | g_ntohl(pack0);
143
144                         switch (pack >> 62) {
145                                 case 1:
146                                         if (file_seek(wth->fh, 1, SEEK_CUR, err) == -1)
147                                                 return FALSE;
148                                         bytes_read = file_read(&stuffing,
149                                                         sizeof stuffing, wth->fh);
150                                         if (bytes_read != sizeof stuffing) {
151                                                 *err = file_error(wth->fh, err_info);
152                                                 return FALSE;
153                                         }
154                                         stuffing &= 0x07;
155                                         packet_size = 14 + stuffing;
156
157                                         {
158                                                 guint64 bytes = pack >> 16;
159                                                 guint64 ts_val =
160                                                         (bytes >> 43 & 0x0007) << 30 |
161                                                         (bytes >> 27 & 0x7fff) << 15 |
162                                                         (bytes >> 11 & 0x7fff) << 0;
163                                                 guint ext = (guint)((bytes >> 1) & 0x1ff);
164                                                 guint64 cr = 300 * ts_val + ext;
165                                                 guint rem = (guint)(cr % SCRHZ);
166                                                 mpeg->now.secs
167                                                         = mpeg->t0 + (time_t)(cr / SCRHZ);
168                                                 mpeg->now.nsecs
169                                                         = (int)(G_GINT64_CONSTANT(1000000000) * rem / SCRHZ);
170                                         }
171                                         ts = mpeg->now;
172                                         break;
173                                 default:
174                                         packet_size = 12;
175                         }
176                 } else {
177                         guint16 length;
178                         bytes_read = file_read(&length, sizeof length, wth->fh);
179                         if (bytes_read != sizeof length) {
180                                 *err = file_error(wth->fh, err_info);
181                                 if (*err == 0 && bytes_read != 0)
182                                         *err = WTAP_ERR_SHORT_READ;
183                                 return FALSE;
184                         }
185                         length = g_ntohs(length);
186                         packet_size = 6 + length;
187                 }
188
189                 if (file_seek(wth->fh, offset, SEEK_SET, err) == -1)
190                         return FALSE;
191         } else {
192                 struct mpa mpa;
193
194                 MPA_UNMARSHAL(&mpa, n);
195                 if (MPA_VALID(&mpa)) {
196                         packet_size = MPA_BYTES(&mpa);
197                         mpeg->now.nsecs += MPA_DURATION_NS(&mpa);
198                         if (mpeg->now.nsecs >= 1000000000) {
199                                 mpeg->now.secs++;
200                                 mpeg->now.nsecs -= 1000000000;
201                         }
202                 } else {
203                         packet_size = mpeg_resync(wth, err, err_info);
204                         if (packet_size == 0)
205                                 return FALSE;
206                 }
207         }
208         *data_offset = file_tell(wth->fh);
209
210         if (!wtap_read_packet_bytes(wth->fh, wth->frame_buffer,
211                                 packet_size, err, err_info))
212                 return FALSE;
213         /* XXX - relative, not absolute, time stamps */
214         wth->phdr.presence_flags = WTAP_HAS_TS;
215         wth->phdr.ts = ts;
216         wth->phdr.caplen = packet_size;
217         wth->phdr.len = packet_size;
218         return TRUE;
219 }
220
221 static gboolean
222 mpeg_seek_read(wtap *wth, gint64 seek_off,
223                 struct wtap_pkthdr *phdr _U_, Buffer *buf, int length,
224                 int *err, gchar **err_info)
225 {
226         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
227                 return FALSE;
228         return wtap_read_packet_bytes(wth->random_fh, buf, length, err, err_info);
229 }
230
231 struct _mpeg_magic {
232         size_t len;
233         const gchar* match;
234 } magic[] = {
235         { 3, "TAG" }, /* ID3v1 */
236         { 3, "ID3" }, /* ID3v2 */
237         { 3, "\0\0\1" }, /* MPEG PES */
238         { 2, "\xff\xfb" }, /* MP3, taken from http://en.wikipedia.org/wiki/MP3#File_structure */
239         { 0, NULL }
240 };
241
242 int
243 mpeg_open(wtap *wth, int *err, gchar **err_info)
244 {
245         int bytes_read;
246         char magic_buf[16];
247         struct _mpeg_magic* m;
248         mpeg_t *mpeg;
249
250         errno = WTAP_ERR_CANT_READ;
251         bytes_read = file_read(magic_buf, sizeof magic_buf, wth->fh);
252         if (bytes_read != (int) sizeof magic_buf) {
253                 *err = file_error(wth->fh, err_info);
254                 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
255                         return -1;
256                 return 0;
257         }
258
259         for (m=magic; m->match; m++) {
260                 if (memcmp(magic_buf, m->match, m->len) == 0)
261                         goto good_magic;
262         }
263
264         return 0;
265
266 good_magic:
267         /* This appears to be a file with MPEG data. */
268         if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
269                 return -1;
270
271         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_MPEG;
272         wth->file_encap = WTAP_ENCAP_MPEG;
273         wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
274         wth->subtype_read = mpeg_read;
275         wth->subtype_seek_read = mpeg_seek_read;
276         wth->snapshot_length = 0;
277
278         mpeg = (mpeg_t *)g_malloc(sizeof(mpeg_t));
279         wth->priv = (void *)mpeg;
280         mpeg->now.secs = 0;
281         mpeg->now.nsecs = 0;
282         mpeg->t0 = mpeg->now.secs;
283
284         return 1;
285 }