1 /*
2  * madplay - MPEG audio decoder and player
3  * Copyright (C) 2000-2004 Robert Leslie
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * $Id: madtime.c,v 1.25 2004/01/23 09:41:32 rob Exp $
20  */
21 
22 # ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 # endif
25 
26 # include "global.h"
27 
28 # if !defined(HAVE_MMAP)
29 #  error "madtime currently requires mmap() support"
30 # endif
31 
32 # include <stdio.h>
33 # include <stdlib.h>
34 
35 # ifdef HAVE_SYS_TYPES_H
36 #  include <sys/types.h>
37 # endif
38 
39 # include <sys/stat.h>
40 
41 # ifdef HAVE_FCNTL_H
42 #  include <fcntl.h>
43 # endif
44 
45 # include <unistd.h>
46 # include <string.h>
47 # include <sys/mman.h>
48 # include <locale.h>
49 # include <mad.h>
50 
51 # include "getopt.h"
52 # include "gettext.h"
53 
54 # if !defined(O_BINARY)
55 #  define O_BINARY  0
56 # endif
57 
58 static
scan(unsigned char const * ptr,unsigned long len,mad_timer_t * duration)59 signed int scan(unsigned char const *ptr, unsigned long len,
60 		mad_timer_t *duration)
61 {
62   struct mad_stream stream;
63   struct mad_header header;
64   unsigned long bitrate, kbps, count;
65   int vbr;
66 
67   mad_stream_init(&stream);
68   mad_header_init(&header);
69 
70   mad_stream_buffer(&stream, ptr, len);
71 
72   bitrate = kbps = count = vbr = 0;
73 
74   while (1) {
75     if (mad_header_decode(&header, &stream) == -1) {
76       if (MAD_RECOVERABLE(stream.error))
77 	continue;
78       else
79 	break;
80     }
81 
82     if (bitrate && header.bitrate != bitrate)
83       vbr = 1;
84 
85     bitrate = header.bitrate;
86 
87     kbps += bitrate / 1000;
88     ++count;
89 
90     mad_timer_add(duration, header.duration);
91   }
92 
93   mad_header_finish(&header);
94   mad_stream_finish(&stream);
95 
96   if (count == 0)
97     count = 1;
98 
99   return ((kbps * 2) / count + 1) / 2 * (vbr ? -1 : 1);
100 }
101 
102 static
calc(char const * path,mad_timer_t * duration,signed int * kbps,unsigned long * kbytes)103 int calc(char const *path, mad_timer_t *duration,
104 	 signed int *kbps, unsigned long *kbytes)
105 {
106   int fd;
107   struct stat stat;
108   void *fdm;
109 
110   fd = open(path, O_RDONLY | O_BINARY);
111   if (fd == -1) {
112     perror(path);
113     return -1;
114   }
115 
116   if (fstat(fd, &stat) == -1) {
117     perror("fstat");
118     close(fd);
119     return -1;
120   }
121 
122   if (!S_ISREG(stat.st_mode)) {
123     fprintf(stderr, _("%s: Not a regular file\n"), path);
124     close(fd);
125     return -1;
126   }
127 
128   *kbytes = (stat.st_size + 512) / 1024;
129 
130   fdm = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
131   if (fdm == MAP_FAILED) {
132     perror("mmap");
133     close(fd);
134     return -1;
135   }
136 
137   if (fdm) {
138     *kbps = scan(fdm, stat.st_size, duration);
139 
140     if (munmap(fdm, stat.st_size) == -1) {
141       perror("munmap");
142       close(fd);
143       return -1;
144     }
145   }
146   else
147     *kbps = 0;
148 
149   if (close(fd) == -1) {
150     perror("close");
151     return -1;
152   }
153 
154   return 0;
155 }
156 
157 static
show(mad_timer_t duration,signed int kbps,unsigned long kbytes,char const * label)158 void show(mad_timer_t duration, signed int kbps,
159 	  unsigned long kbytes, char const *label)
160 {
161   char duration_str[19];
162 
163   mad_timer_string(duration, duration_str,
164 		   "%4lu:%02u:%02u.%1u", MAD_UNITS_HOURS,
165 		   MAD_UNITS_DECISECONDS, 0);
166 
167 # if defined(HAVE_LOCALECONV)
168   {
169     char *point;
170 
171     point = strchr(duration_str, '.');
172     if (point)
173       *point = *localeconv()->decimal_point;
174   }
175 # endif
176 
177   printf(_("%8.1f MB  %c%3u kbps  %s  %s\n"), kbytes / 1024.0,
178 	 kbps < 0 ? '~' : ' ', abs(kbps), duration_str, label);
179 }
180 
181 static
usage(char const * argv0)182 void usage(char const *argv0)
183 {
184   fprintf(stderr, _("Usage: %s [-s] FILE [...]\n"), argv0);
185 }
186 
187 /*
188  * NAME:	main()
189  * DESCRIPTION:	program entry point
190  */
main(int argc,char * argv[])191 int main(int argc, char *argv[])
192 {
193   mad_timer_t total;
194   unsigned long total_kbps, total_kbytes, count;
195   signed int bitrate;
196   int vbr, opt, i, sum_only = 0;
197 
198   /* internationalization support */
199 
200 # if defined(ENABLE_NLS)
201   setlocale(LC_ALL, "");
202   bindtextdomain(PACKAGE, LOCALEDIR);
203   textdomain(PACKAGE);
204 # endif
205 
206   /* initialize and get options */
207 
208   if (argc > 1) {
209     if (strcmp(argv[1], "--version") == 0) {
210       printf("%s - %s\n", mad_version, mad_copyright);
211       printf(_("Build options: %s\n"), mad_build);
212       return 0;
213     }
214     if (strcmp(argv[1], "--help") == 0) {
215       usage(argv[0]);
216       return 0;
217     }
218   }
219 
220   while ((opt = getopt(argc, argv, "s")) != -1) {
221     switch (opt) {
222     case 's':
223       sum_only = 1;
224       break;
225 
226     default:
227       usage(argv[0]);
228       return 1;
229     }
230   }
231 
232   if (optind == argc) {
233     usage(argv[0]);
234     return 1;
235   }
236 
237   /* main processing */
238 
239   total = mad_timer_zero;
240 
241   total_kbps = total_kbytes = count = bitrate = vbr = 0;
242 
243   for (i = optind; i < argc; ++i) {
244     mad_timer_t duration = mad_timer_zero;
245     signed int kbps;
246     unsigned long kbytes;
247 
248     if (calc(argv[i], &duration, &kbps, &kbytes) == -1)
249       continue;
250 
251     if (!sum_only)
252       show(duration, kbps, kbytes, argv[i]);
253 
254     mad_timer_add(&total, duration);
255 
256     total_kbytes += kbytes;
257 
258     if (kbps) {
259       total_kbps += abs(kbps);
260       ++count;
261     }
262 
263     if (kbps < 0 || (bitrate && kbps != bitrate))
264       vbr = 1;
265 
266     bitrate = kbps;
267   }
268 
269   if (count == 0)
270     count = 1;
271 
272   if (argc > 2 || sum_only) {
273     show(total, ((total_kbps * 2) / count + 1) / 2 * (vbr ? -1 : 1),
274 	 total_kbytes, _("TOTAL"));
275   }
276 
277   return 0;
278 }
279