1 /*
2  * mp3stat - scan mp3 for time and other bitrate info built with some code
3  *           from mp3check.
4  * Copyright (c) 2002 by Ed Sweetman <safemode@comcast.net>
5  * mp3check - check mp3 file for consistency and print infos
6  * Copyright (C) 1998 by Johannes Overmann <overmann@iname.com>
7  */
8 #include <unistd.h>
9 #include <sys/stat.h>
10 #include <sys/mman.h>
11 #include <fcntl.h>
12 #include <string>
13 
14 #include "mp3stat.h"
15 
16 using namespace std;
17 
18 int mp3::min_valid = 6;
19 int mp3::FREEFORMAT = 0;
20 int mp3::FORBIDDEN = -1;
21 uint mp3::CONST_MASK = 0xffffffff;
22 int mp3::layer_tab[4] = { 0, 3, 2, 1 };
23 int mp3::bitrate1_tab[16][3] = {
24                          {FREEFORMAT, FREEFORMAT, FREEFORMAT},
25                          {32, 32, 32},
26                          {64, 48, 40},
27 	                 {96, 56, 48},
28 	                 {128, 64, 56},
29 	                 {160, 80, 64},
30 	                 {192, 96, 80},
31 		         {224, 112, 96},
32 		         {256, 128, 112},
33 		         {288, 160, 128},
34 		         {320, 192, 160},
35 			 {352, 224, 192},
36 			 {384, 256, 224},
37 			 {416, 320, 256},
38 			 {448, 384, 320},
39 			 {FORBIDDEN, FORBIDDEN, FORBIDDEN}
40 			 };
41 int mp3::bitrate2_tab[16][3] = {
42                         {FREEFORMAT, FREEFORMAT, FREEFORMAT},
43                         {32, 8, 8},
44                         {48, 16, 16},
45                         {56, 24, 24},
46 	                {64, 32, 32},
47 	                {80, 40, 40},
48 	                {96, 48, 48},
49 	                {112, 56, 56},
50 		        {128, 64, 64},
51 		        {144, 80, 80},
52 		        {160, 96, 96},
53 		        {176, 112, 112},
54 			{192, 128, 128},
55 			{224, 144, 144},
56 			{256, 160, 160},
57 			{FORBIDDEN, FORBIDDEN, FORBIDDEN}
58 			};
59 double   mp3::sampd1_tab[4] = { 44.1, 48.0, 32.0, 0.0 };
60 int      mp3::samp_1_tab[4] = { 44100, 48000, 32000, 50000 };
61 double   mp3::sampd2_tab[4] = { 22.05, 24.0, 16.0, 0.0 };
62 int      mp3::samp_2_tab[4] = { 22050, 24000, 16000, 50000 };
63 
64 // get header from pointer
65 mp3::Header
get_header(const uchar * p)66 mp3::get_header (const uchar * p)
67 {
68   Header              h;
69   uchar              *q = (uchar *) & h;
70   q[0] = p[3];
71   q[1] = p[2];
72   q[2] = p[1];
73   q[3] = p[0];
74   return h;
75 }
76 
77 // set header to pointer
78 void
set_header(uchar * p,Header h)79 mp3::set_header (uchar * p, Header h)
80 {
81   uchar              *q = (uchar *) & h;
82   p[0] = q[3];
83   p[1] = q[2];
84   p[2] = q[1];
85   p[3] = q[0];
86 }
87 
88 // return length of frame in bytes
89 int
frame_length(Header h)90 mp3::frame_length (Header h)
91 {
92   if (h.ID) {
93     return ((((h.layer () == 1) ? 48000 : 144000) * h.bitrate ()) /
94 	    h.samp_int_rate ()) + h.padding_bit;
95   } else {
96     return ((((h.layer () == 1) ? 24000 : 72000) * h.bitrate ()) /
97 	    h.samp_int_rate ()) + h.padding_bit;
98   }
99 }
100 
101 //Detects the next valid header
102 int
find_next_header(const uchar * p,int len,int min_valid)103 mp3::find_next_header (const uchar * p, int len, int min_valid)
104 {
105   int                 i;
106   const uchar        *q = p;
107   const uchar        *t;
108   int                 rest, k, l;
109   Header              h, h2;
110   for (i = 0; i < len - 3; i++, q++) {
111     if (*q == 255) {
112       h = get_header (q);
113       l = frame_length (h);
114       if (h.isValid () && (l >= 21)) {
115 	t = q + l;
116 	rest = len - i - l;
117 	for (k = 1; (k < min_valid) && (rest >= 4); k++) {
118 	  h2 = get_header (t);
119 	  if (!h2.isValid ())
120 	    break;
121 	  if (!h2.sameConstant (h))
122 	    break;
123 	  l = frame_length (h2);
124 	  if (l < 21)
125 	    break;
126 	  t += l;
127 	  rest -= l;
128 	}
129 	if (k == min_valid)
130 	  return i;
131       }
132     }
133   }
134   return -1;			// not found
135 }
136 
137 // Major function ...scans mp3 and gathers info as it does it
138 void
scan_mp3(uchar * p,int len,statistic * mp3info2)139 mp3::scan_mp3 (uchar * p, int len, statistic * mp3info2)
140 {
141   int                 start = find_next_header (p, len, min_valid);
142   int                 rest = len;
143   int                 l, s;
144 
145   if (start >= 0) {
146     // check whole file
147     rest -= start;
148     p += start;
149     Header              head = get_header (p);
150     l = frame_length (head);
151     p += l;
152     rest -= l;
153     start += l;
154     while (rest >= 4) {
155       Header              h = get_header (p);
156       mp3info2->addBit(h.bitrate());
157       if (!(h.isValid () && (frame_length (h) >= 21))) {
158 	// invalid header, search for next valid header
159 	s = find_next_header (p, rest, min_valid);
160 	if (s < 0)
161 	  break;		// error: junk at eof
162 	// skips invalid bytes
163 	p += s;
164 	rest -= s;
165 	start += s;
166       } else {
167 	head = h;
168 	// skip to next frame
169 	l = frame_length (h);
170 	p += l;
171 	rest -= l;
172 	start += l;
173       }
174     }
175   }
176 }
177 
178 //  mmaps mp3 calls scan_mp3 which returns filled status structure mp3info
179 void
statfile(statistic * mp3info2)180 mp3::statfile (statistic * mp3info2)
181 {
182   //create stat accessor
183   struct stat         buf;
184   int                 len;
185   int                 fd;
186   uchar              *p;
187   uchar              *free_p;
188   stat (mp3info2->getName().c_str(), &buf);
189   len = buf.st_size;
190   mp3info2->setSize((double) len / 1024);
191   // open file
192   fd = open (mp3info2->getName().c_str(), O_RDONLY);
193   if (fd == -1) {
194     return;
195   }
196   // mmap file
197   p = (uchar *) mmap (0, len, PROT_READ, MAP_SHARED, fd, 0);
198   free_p = p;			//this should be left here, scan_mp3 is modifying p
199   if (p == (uchar *) - 1) {
200     return;
201   }
202   scan_mp3 (p, len, mp3info2);
203   // unmap file and close
204   if (munmap ((char *) free_p, len)) {
205     return;
206   }
207   close (fd);
208 }
209 
createi()210 extern "C" input* createi() {
211    return new mp3;
212 }
213 
destroyi(input * tempIn)214 extern "C" void destroyi(input* tempIn) {
215    delete tempIn;
216 }
217