1 /*-
2 * Copyright (c) 2001 Yar Tikhiy <yar@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35
36 #ifndef lint
37 static const char rcsid[] =
38 "$Id: mp3ck.c,v 1.4 2005/04/03 04:49:22 yarq Exp $";
39 #endif
40
41 #define MPEG_HDR_SIZE 4
42 #define MPEG_SPF 1152L /* samples per frame XXX: not true for L1 */
43
44 #define MPEG_VERID(c) (((c) >> 3) & 3)
45 #define MPEG_LAYERD(c) (((c) >> 1) & 3)
46 #define MPEG_PROT(c) ((c) & 1)
47
48 #define MPEG_BRIDX(c) (((c) >> 4) & 0xf)
49 #define MPEG_FRIDX(c) (((c) >> 2) & 3)
50 #define MPEG_PAD(c) (((c) >> 1) & 1)
51 #define MPEG_PRIV(c) ((c) & 1)
52
53 #define MPEG_CHAN(c) (((c) >> 6) & 3)
54 #define MPEG_MODEX(c) (((c) >> 4) & 3)
55 #define MPEG_CPRT(c) (((c) >> 3) & 1)
56 #define MPEG_ORIG(c) (((c) >> 2) & 1)
57 #define MPEG_EMPH(c) ((c) & 3)
58
59 #define MPEG_LAYER(c) (4 - MPEG_LAYERD(c))
60 /*
61 * NB: ver == 4 for MPEG2.5
62 */
63 #define MPEG_VER(c) (4 - MPEG_VERID(c))
64 #define MPEG_BRSELECTOR(ver, layer) \
65 ((ver) == 1 ? (layer) - 1 : ((layer) == 1 ? 3 : 4))
66 #define MPEG_BITRATE(c, ver, layer) \
67 (bitratetab[MPEG_BRSELECTOR((ver), (layer))][MPEG_BRIDX(c)])
68 #define MPEG_FREQ(c, ver) \
69 (freqtab[(ver) == 4 ? 2 : (ver) - 1][MPEG_FRIDX(c)])
70
71 #define ID3V1_SIZE 128
72 #define ID3V1_LABEL_LEN 3
73
74 #define ID3V2_LABEL_LEN 3
75 /*
76 * This struct doesn't include the "ID3" label on purpose.
77 * See the function id3v2().
78 */
79 struct id3v2 {
80 /* unsigned char id3[3]; */
81 unsigned char major;
82 unsigned char revision;
83 unsigned char flags;
84 unsigned char size[4];
85 };
86
87 #define ID3V2_FLAG_UNSYNC 0x80
88 #define ID3V2_FLAG_EXTHDR 0x40
89 #define ID3V2_FLAG_EXP 0x20
90 #define ID3V2_FLAG_FOOTER 0x10
91
92 #define SYNCSAFE32(n) ((n)[3] + 128 * ((n)[2] + 128 * ((n)[1] + 128 * (n)[0])))
93
94 #define MSG_NONE 0 /* don't forget to update this! */
95 #define MSG_ERR 0
96 #define MSG_WRN 1
97 #define MSG_NTC 2
98 #define MSG_NFO 3
99 #define MSG_DBG 4
100 #define MSG_ALL 4 /* don't forget to update this! */
101
102 int pedantic = 0; /* treat some warnings as errors */
103 int time_offset = 0; /* show offsets as mm:ss if possible */
104 int verbosity = MSG_NTC; /* how much output to produce */
105
106 static int check(const char *);
107 static void frametotime(long, long *, long *);
108 static void usage(void);
109
110 int
main(int argc,char ** argv)111 main(int argc, char **argv)
112 {
113 int opt;
114 int rc, maxrc;
115
116 while ((opt = getopt(argc, argv, "dhpqstv")) != -1)
117 switch (opt) {
118 case 'd':
119 verbosity = MSG_ALL;
120 break;
121 case 'p':
122 pedantic = 1;
123 break;
124 case 'q':
125 verbosity--;
126 break;
127 case 's':
128 verbosity = MSG_NONE;
129 break;
130 case 't':
131 time_offset = 1;
132 break;
133 case 'v':
134 verbosity++;
135 break;
136 case 'h':
137 case '?':
138 usage();
139 /* NOTREACHED */
140 }
141
142 argc -= optind;
143 argv += optind;
144 if (argc < 1)
145 usage();
146
147 maxrc = 0;
148 for (; *argv; argv++) {
149 rc = check(*argv);
150 if (rc > maxrc)
151 maxrc = rc;
152 }
153
154 /*
155 * 0 - OK
156 * 1 - MPEG format error
157 * 2 - file or system error
158 */
159 if (maxrc > 2)
160 maxrc = 2;
161 exit(maxrc);
162 return (0); /* make cc happy */
163 }
164
165 long bitratetab[][16] = {
166 /* 0: V1, L1 */
167 { -1, 32000L, 64000L, 96000L, 128000L, 160000L, 192000L, 224000L,
168 256000L, 288000L, 320000L, 352000L, 384000L, 416000L, 448000L, -1 },
169 /* 1: V1, L2 */
170 { -1, 32000L, 48000L, 56000L, 64000L, 80000L, 96000L, 112000L,
171 128000L, 160000L, 192000L, 224000L, 256000L, 320000L, 384000L, -1 },
172 /* 2: V1, L3 */
173 { -1, 32000L, 40000L, 48000L, 56000L, 64000L, 80000L, 96000L,
174 112000L, 128000L, 160000L, 192000L, 224000L, 256000L, 320000L, -1 },
175 /* 3: V2, L1 */
176 { -1, 32000L, 48000L, 56000L, 64000L, 80000L, 96000L, 112000L,
177 128000L, 144000L, 160000L, 176000L, 192000L, 224000L, 256000L, -1 },
178 /* 4: V2, L2 & L3 */
179 { -1, 8000L, 16000L, 24000L, 32000L, 40000L, 48000L, 56000L,
180 64000L, 80000L, 96000L, 112000L, 128000L, 144000L, 160000L, -1 }
181 };
182
183 long freqtab[][4] = {
184 { 44100L, 48000L, 32000L, -1 },
185 { 22050L, 24000L, 16000L, -1 },
186 { 11025L, 12000L, 8000L, -1 }
187 };
188
189 enum state {START, SYNC, FRAME, ID3V1, ID3V2, FINISH, QUIT, NOSTATE};
190
191 const char *file; /* current file name */
192 FILE *fp; /* current file handle */
193 int c; /* current byte */
194 int insync; /* if we're seeing what we expected */
195 long goodend; /* offset right beyond last good frame or tag */
196 long nframes; /* total audio frames seen */
197 long where; /* starting offset of current frame/tag (for wrn()) */
198 int rc; /* what to return to upper layer */
199 int vbr; /* current file has VBR */
200
201 /* for final info/statistics */
202 long kbps; /* sum of kbps of seen frames if VBR */
203 long hz;
204 int chanmode;
205 int hascrc;
206 int hasid3v1;
207 int hasid3v2;
208
209 static enum state start(void);
210 static enum state sync1(void);
211 static enum state frame(void);
212 static enum state id3v1(void);
213 static enum state id3v2(void);
214 static enum state finish(void);
215
216 static void wrn(int, long, const char *, ...)
217 #ifdef __GNUC__
218 __attribute__ ((format (printf, 3, 4)))
219 #endif
220 ;
221 static void wrnsync(void);
222 static void diffhdr(const char *, const char *);
223
224 static int
check(const char * fn)225 check(const char *fn)
226 {
227 enum state state;
228
229 file = fn;
230
231 for (state = START; state != QUIT;)
232 switch (state) {
233 case START:
234 state = start();
235 break;
236 case SYNC:
237 state = sync1();
238 break;
239 case FRAME:
240 state = frame();
241 break;
242 case ID3V1:
243 state = id3v1();
244 break;
245 case ID3V2:
246 state = id3v2();
247 break;
248 case FINISH:
249 state = finish();
250 break;
251 default:
252 printf("Can't happen: unknown state\n");
253 exit(2);
254 }
255 return (rc);
256 }
257
258 static enum state
start()259 start()
260 {
261
262 if ((fp = fopen(file, "rb")) == NULL) {
263 rc = 2;
264 perror(file);
265 return (QUIT);
266 }
267
268 c = EOF;
269 insync = 1;
270 goodend = 0;
271 nframes = 0;
272 hasid3v1 = hasid3v2 = 0;
273 vbr = 0;
274 rc = 0;
275
276 return (SYNC);
277 }
278
279 /*
280 * When transitioning to this state:
281 * Set `c' to EOF to proceed to the next byte.
282 * Leave `c' untouched to restart sync'ing from the current position.
283 */
284 static enum state
sync1()285 sync1()
286 {
287
288 if (c == EOF) /* allow restarting from the same stream position */
289 c = getc(fp);
290
291 where = ftell(fp) - 1;
292
293 switch (c) {
294 case EOF:
295 if (ftell(fp) == 0) {
296 rc = 1;
297 wrn(MSG_WRN, -1, "empty file");
298 } else if (nframes == 0) {
299 rc = 1;
300 wrn(MSG_WRN, -1, "no audio frames");
301 } else if (!insync) {
302 if (pedantic)
303 rc = 1;
304 wrn(MSG_NTC, goodend, "trailing junk");
305 }
306 return (FINISH);
307 /* NOTREACHED */
308
309 case 'I':
310 if (insync)
311 return (ID3V2);
312 else {
313 c = EOF;
314 return (SYNC);
315 }
316 /* NOTREACHED */
317
318 case 'T':
319 if (insync)
320 return (ID3V1);
321 else {
322 c = EOF;
323 return (SYNC);
324 }
325 /* NOTREACHED */
326
327 case 0xff:
328 return (FRAME);
329 /* NOTREACHED */
330
331 default:
332 wrnsync();
333 insync = 0;
334 c = EOF;
335 return (SYNC);
336 /* NOTREACHED */
337 }
338
339 return (NOSTATE);
340 }
341
342 static enum state
frame()343 frame()
344 {
345 int i;
346 int ver, layer, prot, pad;
347 long bitrate, freq;
348 long framelen, n;
349 char hdr[MPEG_HDR_SIZE - 1];
350 static char phdr[MPEG_HDR_SIZE - 1];
351
352 i = 0;
353
354 if ((c = getc(fp)) == EOF) {
355 rc = 1;
356 wrn(MSG_WRN, 0, "unexpected EOF at frame header+1");
357 return (FINISH);
358 }
359 if ((c & 0xe0) != 0xe0) {
360 wrnsync();
361 insync = 0;
362 return (SYNC);
363 }
364 hdr[i++] = c;
365
366 ver = MPEG_VER(c);
367 switch (ver) {
368 case 1:
369 case 2:
370 case 4: /* 2.5 */
371 break;
372 case 3:
373 rc = 1;
374 wrn(MSG_WRN, 0, "invalid MPEG version ID");
375 insync = 0;
376 return (SYNC);
377 /* NOTREACHED */
378 default:
379 abort();
380 /* NOTREACHED */
381 }
382
383 layer = MPEG_LAYER(c);
384 switch (layer) {
385 case 1:
386 rc = 1;
387 wrn(MSG_WRN, 0, "layer I unsupported");
388 insync = 0;
389 return (SYNC);
390 /* NOTREACHED */
391 case 2:
392 case 3:
393 break;
394 case 4:
395 rc = 1;
396 wrn(MSG_WRN, 0, "invalid layer description");
397 insync = 0;
398 return (SYNC);
399 /* NOTREACHED */
400 default:
401 abort();
402 /* NOTREACHED */
403 }
404
405 prot = MPEG_PROT(c);
406 #if notyet
407 crc = 0xffff;
408 #endif
409
410 if ((c = getc(fp)) == EOF) {
411 rc = 1;
412 wrn(MSG_WRN, 0, "unexpected EOF at frame header+2");
413 return (FINISH);
414 }
415 #if notyet
416 if (prot)
417 crcupdate(c, &crc);
418 #endif
419 hdr[i++] = c;
420
421 bitrate = MPEG_BITRATE(c, ver, layer);
422 if (bitrate < 0) {
423 rc = 1;
424 wrn(MSG_WRN, 0, "invalid bitrate index");
425 insync = 0;
426 return (SYNC);
427 }
428
429 freq = MPEG_FREQ(c, ver);
430 if (freq < 0) {
431 rc = 1;
432 wrn(MSG_WRN, 0, "invalid sampling frequency index");
433 insync = 0;
434 return (SYNC);
435 }
436
437 pad = MPEG_PAD(c);
438
439 if ((c = getc(fp)) == EOF) {
440 rc = 1;
441 wrn(MSG_WRN, 0, "unexpected EOF at frame header+3");
442 return (FINISH);
443 }
444 #if notyet
445 if (prot)
446 crcupdate(c, &crc);
447 #endif
448 hdr[i++] = c;
449
450 framelen = MPEG_SPF / 8 * bitrate / freq + pad;
451
452 if (!insync) {
453 rc = 1;
454 wrn(MSG_WRN, 0,
455 "sync after %ld bytes of junk,", where - goodend);
456 }
457 wrn(MSG_DBG, -1, "frame br %ld hz %ld pad %d%s len %ld "
458 "at %#lx next %#lx",
459 bitrate, freq, pad, prot ? " CRC" : "", framelen,
460 ftell(fp) - MPEG_HDR_SIZE,
461 ftell(fp) - MPEG_HDR_SIZE + framelen);
462 if (nframes) {
463 diffhdr(phdr, hdr);
464 if (vbr)
465 kbps += bitrate / 1000;
466 } else {
467 kbps = bitrate / 1000;
468 hz = freq;
469 hascrc = prot;
470 chanmode = MPEG_CHAN(c);
471 }
472
473 memcpy(phdr, hdr, sizeof(hdr) / sizeof(hdr[0]));
474
475 /* skip the rest of the frame */
476 for (n = framelen - MPEG_HDR_SIZE; n; n--)
477 if ((c = getc(fp)) == EOF) {
478 rc = 1;
479 wrn(MSG_WRN, 0, "last frame incomplete "
480 "(%ld of %ld bytes missing)", n, framelen);
481 return (FINISH);
482 }
483
484 c = EOF;
485 insync = 1;
486 goodend = ftell(fp);
487 nframes++;
488 return (SYNC);
489 }
490
491 static enum state
id3v1()492 id3v1()
493 {
494 int n;
495
496 if ((c = getc(fp)) == EOF) {
497 if (pedantic)
498 rc = 1;
499 wrn(MSG_NTC, goodend, "trailing junk (truncated ID3v1 tag?)");
500 return (FINISH);
501 }
502 if (c != 'A') {
503 insync = 0;
504 return (SYNC);
505 }
506 if ((c = getc(fp)) == EOF) {
507 if (pedantic)
508 rc = 1;
509 wrn(MSG_NTC, goodend, "trailing junk (truncated ID3v1 tag?)");
510 return (FINISH);
511 }
512 if (c != 'G') {
513 insync = 0;
514 return (SYNC);
515 }
516
517 hasid3v1 = 1;
518 wrn(MSG_DBG, -1, "ID3v1 tag at %#lx", ftell(fp) - 3);
519
520 for (n = ID3V1_SIZE - ID3V1_LABEL_LEN; n; n--)
521 if ((c = getc(fp)) == EOF) {
522 rc = 1;
523 wrn(MSG_WRN, 0, "ID3v1 tag incomplete "
524 "(%d of %d bytes missing)",
525 n, ID3V1_SIZE);
526 return (FINISH);
527 }
528 if ((c = getc(fp)) == EOF)
529 return (FINISH);
530
531 rc = 1;
532 wrn(MSG_WRN, 0, "ID3v1 in the middle of the file");
533 insync = 1;
534 return (SYNC);
535 }
536
537 static enum state
id3v2()538 id3v2()
539 {
540 long idlen, n;
541 struct id3v2 id;
542
543 if ((c = getc(fp)) == EOF) {
544 if (pedantic)
545 rc = 1;
546 wrn(MSG_NTC, goodend, "trailing junk (truncated ID3v2 tag?)");
547 return (FINISH);
548 }
549 if (c != 'D') {
550 wrnsync();
551 insync = 0;
552 return (SYNC);
553 }
554 if ((c = getc(fp)) == EOF) {
555 if (pedantic)
556 rc = 1;
557 wrn(MSG_NTC, goodend, "trailing junk (truncated ID3v2 tag?)");
558 return (FINISH);
559 }
560 if (c != '3') {
561 wrnsync();
562 insync = 0;
563 return (SYNC);
564 }
565
566 hasid3v2 = 1;
567
568 if (fread(&id, sizeof(id), 1, fp) != 1) {
569 rc = 1;
570 wrn(MSG_WRN, 0, "ID3v2 tag incomplete");
571 return (FINISH);
572 }
573 if (id.major < 2 || id.major > 4) {
574 rc = 1;
575 wrn(MSG_WRN, 0, "ID3v2.%d.%d unsupported", id.major, id.revision);
576 insync = 0;
577 return (SYNC);
578 }
579
580 idlen = ID3V2_LABEL_LEN + sizeof(id) + SYNCSAFE32(id.size);
581 if (id.flags & ID3V2_FLAG_FOOTER)
582 idlen += ID3V2_LABEL_LEN + sizeof(id);
583
584 wrn(MSG_DBG, -1, "ID3v2.%d.%d tag len %ld at %#lx next %#lx",
585 id.major, id.revision, idlen,
586 ftell(fp) - ID3V2_LABEL_LEN - sizeof(id),
587 ftell(fp) - ID3V2_LABEL_LEN - sizeof(id) + idlen);
588
589 for (n = idlen - ID3V2_LABEL_LEN - sizeof(id); n; n--)
590 if ((c = getc(fp)) == EOF) {
591 rc = 1;
592 wrn(MSG_WRN, 0, "ID3v2 tag incomplete "
593 "(%ld of %ld bytes missing)", n, idlen);
594 return (FINISH);
595 }
596 c = EOF;
597 insync = 1;
598 goodend = ftell(fp);
599 return (SYNC);
600 }
601
602 static enum state
finish()603 finish()
604 {
605 long mm, ss;
606 const char *st;
607
608 if (ferror(fp)) {
609 rc = 2;
610 perror(file);
611 }
612 fclose(fp);
613
614 if (nframes) {
615 frametotime(nframes, &mm, &ss);
616 switch (chanmode) {
617 case 0:
618 st = "stereo";
619 break;
620 case 1:
621 st = "joint stereo";
622 break;
623 case 2:
624 st = "dual stereo";
625 break;
626 case 3:
627 st = "mono";
628 break;
629 default:
630 abort();
631 }
632 wrn(MSG_NFO, -1, "%s%ld kbps %ld hz %s%s%s%s (%ld:%02ld)",
633 vbr ? "VBR/" : "",
634 vbr ? kbps / nframes : kbps,
635 hz, st,
636 hascrc ? " CRC" : "",
637 hasid3v1 ? " ID3v1" : "",
638 hasid3v2 ? " ID3v2" : "",
639 mm, ss);
640 }
641
642 return (QUIT);
643 }
644
645 static void
wrn(int level,long offset,const char * fmt,...)646 wrn(int level, long offset, const char *fmt, ...)
647 {
648 long mm, ss;
649 va_list ap;
650
651 if (level > verbosity)
652 return;
653 va_start(ap, fmt);
654 printf("%s: ", file);
655 vprintf(fmt, ap);
656 if (offset >= 0) {
657 if (time_offset && offset == 0) {
658 frametotime(nframes, &mm, &ss);
659 printf(" at %ld:%02ld", mm, ss);
660 } else
661 printf(" at byte offset %#lx", offset ? offset : where);
662 }
663 printf("\n");
664 va_end(ap);
665 }
666
667 /*
668 * Don't print the same "lost sync" twice.
669 */
670 static void
wrnsync()671 wrnsync()
672 {
673 if (insync) {
674 if (goodend == 0) {
675 rc = 1;
676 wrn(MSG_WRN, -1, "leading junk");
677 } else
678 /*
679 * warning on lost sync is issued after
680 * its nature gets clear: leading or trailing junk,
681 * broken middle of file etc.
682 */
683 wrn(MSG_DBG, 0, "lost sync");
684 }
685 }
686
687 /*
688 * Check MPEG header fields that must be consistent
689 * through a whole file.
690 */
691 static void
diffhdr(const char * h1,const char * h2)692 diffhdr(const char *h1, const char *h2)
693 {
694
695 #define DIFFTEST(byte, macro, msg) \
696 if (MPEG_##macro(h1[byte]) != MPEG_##macro(h2[byte])) { \
697 rc = 1; \
698 wrn(MSG_WRN, 0, "MPEG " msg " inconsistency (%d -> %d)",\
699 MPEG_##macro(h1[byte]), MPEG_##macro(h2[byte])); \
700 }
701
702 #define DIFFTEST2(byte, macro, msg) \
703 if (MPEG_##macro(h1[byte], MPEG_VER(h1[0])) != \
704 MPEG_##macro(h2[byte], MPEG_VER(h2[0]))) { \
705 rc = 1; \
706 wrn(MSG_WRN, 0, "MPEG " msg " inconsistency (%ld -> %ld)",\
707 MPEG_##macro(h1[byte], MPEG_VER(h1[0])), \
708 MPEG_##macro(h2[byte], MPEG_VER(h2[0]))); \
709 }
710
711 DIFFTEST(0, VERID, "version ID");
712 DIFFTEST(0, LAYERD, "layer descriptor");
713 DIFFTEST(0, PROT, "protection bit");
714 /*DIFFTEST2(1, BITRATE, "bitrate");*/
715 if (!vbr &&
716 MPEG_BITRATE(h1[1], MPEG_VER(h1[0]), MPEG_LAYER(h1[0])) !=
717 MPEG_BITRATE(h2[1], MPEG_VER(h2[0]), MPEG_LAYER(h2[0]))) {
718 vbr = 1;
719 kbps *= nframes; /* if VBR, kbps holds sum of bitrates */
720 }
721 DIFFTEST2(1, FREQ, "sampling rate frequency");
722 DIFFTEST(1, PRIV, "private bit");
723 DIFFTEST(2, CHAN, "channel mode");
724 DIFFTEST(2, CPRT, "copyright bit");
725 DIFFTEST(2, ORIG, "original bit");
726 DIFFTEST(2, EMPH, "emphasis");
727 }
728
729 static void
frametotime(long frame,long * min,long * sec)730 frametotime(long frame, long *min, long *sec)
731 {
732 long t;
733
734 if (frame)
735 t = frame * MPEG_SPF / hz; /* XXX uses global var */
736 else
737 t = 0; /* Don't use hz in this case since it is 0 */
738 *min = t / 60;
739 *sec = t % 60;
740 }
741
742 static void
usage()743 usage()
744 {
745 printf("usage: mp3ck [-dhqstv] file ...\n");
746 exit(2);
747 }
748