1 /*
2 * mp3plot - Bitrate analysis tool
3 *
4 * Copyright (C) 2007, 2009 Toni Corvera
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 // $Id: frame.cc 1347 2009-06-22 15:53:15Z toni $
22
23 #include <iostream>
24 #include <cassert>
25 #include <cstring> // memcmp
26
27 #include "../mp3_config.h" // btoh32
28 #include "frame.h"
29
30 // References:
31 // [[HA1]] Frame Size, Calc bitrate, length, vbr/cbr etc..
32 // <http://www.hydrogenaudio.org/forums/index.php?showtopic=52216>
33 // [[HA2]] Is this a MP3 Frame header
34 // <http://www.hydrogenaudio.org/forums/index.php?showtopic=43172>
35
36 using std::ifstream;
37 using std::string;
38 using std::ios;
39 using std::cout;
40
41 namespace {
42 // These are used to avoid showing a warning for each frame tainting the results
43 int unsupported_frequency = 0;
44 int bad_bitrate = 0;
45 int freeform_bitrate = 0;
46 int mpeg25 = 0;
47 }
48
49 namespace mp3plot {
50
51 namespace mp3 {
52
53 using net_outlyer::btoh32;
54
55 // -- MP3 Frame --
56
read(ifstream & in)57 void frame::read(ifstream & in) throw (e_error) {
58 using std::cerr;
59 using std::endl;
60
61 uint8_t sync_header[4];
62 in.read(CHARR(sync_header), 4);
63 if (!in.good()) {
64 throw e_read_failure();
65 }
66
67 // The sync part is 12 bits, starting on byte-boundary
68 // ((11 bits for the non-standard MPEG 2.5) set to 1
69 if (sync_header[0] != 0xff || (sync_header[1]&0xf0)!=0xf0) {
70 throw e_read_failure("Invalid frame start.", EX_DATAERR);
71 }
72
73 assert( in.tellg() >= 4 );
74 start = -4;
75 start += in.tellg(); // compiler complains if done in one operation
76
77 // MPEG Version
78 {
79 // 2nd byte: xxxY Yxxx
80 uint8_t version_field = (sync_header[1] & 0x18) >> 3;
81 mpeg_version = mpeg_version_t(version_field);
82 // TODO: My MPEG2.5 sample doesn't seem to match this, might be a sync issue
83 if (MPEG2_5 == mpeg_version && 0 == mpeg25) {
84 // FIXME: Add support
85 cerr << "WARNING: MPEG 2.5 extensions detected. Information *WILL* be innacurate." << endl;
86 ++mpeg25;
87 }
88
89 // 2nd byte: xxxx xYYx
90 uint8_t layer_field = (sync_header[1] & 0x06) >> 1;
91 mpeg_layer = mpeg_layer_t(layer_field);
92
93 // 2nd byte: xxxx xxxY
94 // This works in reverse, 1 means NO CRC
95 crc = 1 != (sync_header[1] & 0x01);
96
97 // 3rd byte: YYYY xxxx
98 bitrate_index = ( (sync_header[2] & 0xf0) >> 4 );
99 assert( bitrate_index < 0x10 );
100 bitrate_ = bitrate_translator::get(bitrate_index, mpeg_version, mpeg_layer);
101
102 // 3rd byte: xxxx YYxx
103 uint8_t freq_field = (sync_header[2] & 0x0c) >> 2;
104 switch (freq_field) { // FIXME: The table is different for MPEG-2
105 case 0: freq=44100; break;
106 case 1: freq=48000; break;
107 case 2: freq=32000; break;
108 case 3: freq=0; break; // reserved
109 default:
110 assert( false );
111 freq=-1;
112 ;
113 }
114
115 // Adjust according to MPEG version
116 if (MPEG2_5 == mpeg_version) {
117 freq /= 4;
118 }
119 else if (MPEG2 == mpeg_version) {
120 freq /= 2;
121 }
122
123 // 3rd byte: xxxx xxYx
124 has_padding = 0 != (sync_header[2] & 0x02);
125
126 // 3rd byte: xxxx xxxY
127 // Private bit, can be safely discarded
128
129 // 4th byte: YYxx xxxx
130 channel_mode = static_cast<channel_mode_t>( (sync_header[3] & 0xc0)>>6 );
131
132 if (bitrate_ == 0) {
133 if (0 == freeform_bitrate) {
134 cerr << "WARNING: Free-format or corrupted bitstream detected.\n"
135 "\tInformation might be innacurate." << endl;
136 }
137 ++freeform_bitrate;
138 }
139 else if (bitrate_ == bitrate_translator::bad_bitrate()) {
140 if (0 == bad_bitrate) {
141 cerr << "WARNING: Illegal value found in the bitrate field.\n"
142 "\tThis is usually a sign of a corrupted file.\n"
143 "\tInformation might be innacurate." << endl;
144 }
145 ++bad_bitrate;
146 }
147
148 #if 0 // FIXME: This breaks the frame detection. Why?
149 if (crc) {
150 // CRC is stored as 16bits immediately following the header
151 in.seekg(2, ios::cur);
152 }
153 #endif
154 // TODO: parse the other fields
155 }
156 }
157
skip_data(ifstream & in)158 void frame::skip_data(ifstream & in) throw (e_read_failure) {
159 using std::cerr;
160 using std::endl;
161
162 uint16_t f = freq;
163
164 if (f == 0) {
165 f = 44100;
166 if (0 == unsupported_frequency) {
167 cerr << "WARNING: Unsupported frequency found, using a sane default.\n"
168 "\tInformation might be innacurate.\n"
169 "\tThis warning won't be reported again." << endl;
170 }
171 ++unsupported_frequency;
172 }
173
174 // Per PRE not allowed
175 assert( bitrate_translator::bad_bitrate() != bitrate_ );
176 if (bitrate_translator::bad_bitrate() == bitrate_) {
177 throw e_read_failure("Attempted to read a frame with bad bitrate");
178 }
179
180 // Ditto
181 assert( 0 != bitrate_ );
182 if (0 == bitrate_) {
183 throw e_read_failure("Attempted to read a freeform bitstream frame");
184 }
185
186
187 const uint16_t factor = get_sample_size() / 8;
188 assert( factor==48 || factor == 72 || factor == 144 );
189
190 uint32_t framelen = int(factor * (bitrate_ * 1000) / f ) -4;
191 if (has_padding) {
192 // In layer I padding is 4 bytes (that's why the formula is usually given as
193 // '(12 * br / freq + pad) * 4')
194 if (mpeg_layer == LAYER_I) {
195 framelen += 4;
196 }
197 else {
198 ++framelen;
199 }
200 }
201
202 in.seekg(framelen, ios::cur);
203 }
204
205 // See [[HA1]], [[HA2]]
206 // static
get_sample_size(mpeg_version_t v,mpeg_layer_t l)207 uint16_t frame::get_sample_size(mpeg_version_t v, mpeg_layer_t l) {
208 // Layer I (MPEG 1 and 2) has 384 samples per frame,
209 // MPEG-1 Layer II and III and MPEG-2 Layer 2 have 1152 samples per frame
210 // MPEG-2 Layer II has 576 samples per frame
211
212 if (l == LAYER_I) {
213 // MPEG-1/2 Layer I
214 return 384;
215 }
216 if (l == LAYER_III && v == MPEG2) {
217 // MPEG-2 LSF Layer III
218 return 576;
219 }
220 // MPEG-1 Layers II and III, MPEG-2 Layer II
221 return 1152;
222 }
223
read(ifstream & in)224 void frame_first::read(ifstream & in) throw (e_error) {
225 frame::read(in);
226
227 using std::cerr;
228
229 // POST ]-> At end of frame header (4th byte, [3])
230
231 check_vbr(in);
232 if (!info.is_vbr) {
233 // Note even CBR files can have some VBR due to bit reservoir
234 cerr << "This appears to be a CBR file" << std::endl;
235 }
236
237 // POST ]-> At 41th frame byte ([40]) || !is_vbr
238
239 { // TODO: Is this right?
240 in.seekg(3, std::ios_base::cur);
241 in.read(CHARR(&flags), 1);
242 }
243
244 info.has_filesize = (0 != (flags & FL_FILESIZE));
245 info.has_numframes = (0 != (flags & FL_NUMFRAMES));
246 info.has_toc = (0 != (flags & FL_TOC));
247 info.has_vbr_scale = (0 != (flags & FL_VBR_SCALE));
248
249 // POST ]-> At 45th frame byte ([44])
250 {
251 if (info.has_numframes) {
252 in.read(CHARR(&info.num_frames), 4);
253 // XXX: is this ok in all arches?
254 info.num_frames = btoh32(info.num_frames);
255 }
256 else {
257 in.seekg(4, ios::cur);
258 }
259
260 if (info.has_filesize) {
261 in.read(CHARR(&info.file_size), 4);
262 info.file_size = btoh32(info.file_size);
263 }
264 else {
265 in.seekg(4, ios::cur);
266 }
267
268 // FIXME: Do something with the TOC ?
269 in.seekg(99, ios::cur);
270 // FIXME: Do something with VBR Scale ?
271 in.seekg(4, ios::cur);
272 }
273 }
274
check_vbr(ifstream & in)275 void frame_first::check_vbr(ifstream & in) throw (e_read_failure) {
276 // PRE ]-> At 4th byte [3]
277
278 const int BLOCK=36;
279 uint8_t buffer[BLOCK];
280 in.read(CHARR(buffer), BLOCK);
281
282 int offset=-1;
283 while (!info.is_vbr && ++offset < (BLOCK-3)) {
284 uint8_t * ptr = & (buffer[offset]);
285
286 if (0 == memcmp(ptr, "Xing", 4)) {
287 info.is_vbr = true;
288 xing_offset = offset + 4; // We started after the first four bytes
289 }
290 }
291 // FIXME: do validity check of offset
292 // // <http://www.multiweb.cz/twoinches/MP3inside.htm>
293 // 36 "Xing" for MPEG1 and CHANNEL != mono (most used)
294 // 21 "Xing" for MPEG1 and CHANNEL == mono
295 // 21 "Xing" for MPEG2 and CHANNEL != mono
296 // 13 "Xing" for MPEG2 and CHANNEL == mono
297
298 if (!info.is_vbr) { // Return to the CBR frame start
299 in.seekg( -40, std::ios_base::cur );
300 }
301 }
302
operator <<(std::ostream & os,const frame & f)303 std::ostream & operator<<(std::ostream & os, const frame & f) {
304 string ver, layer, chanmode;
305
306 switch (f.mpeg_version) {
307 case MPEG1: ver="MPEG v1"; break;
308 case MPEG2: ver="MPEG v2"; break;
309 case MPEG2_5: ver="MPEG v2.5"; break;
310 default: ver="undefined";
311 }
312 switch (f.mpeg_layer) {
313 case LAYER_III: layer="Layer III"; break;
314 case LAYER_II: layer="Layer II"; break;
315 case LAYER_I: layer="Layer I"; break;
316 default: layer="undefined";
317 }
318 switch (f.channel_mode) {
319 case STEREO: chanmode="Stereo"; break;
320 case JOINT_STEREO: chanmode="Joint Stereo"; break;
321 case DUAL_MONO: chanmode="Dual Mono"; break;
322 case MONO: chanmode="Mono"; break;
323 default: chanmode="undefined"; break;
324 }
325
326 os << "MPEG Version: " << ver << "\n";
327 os << "MPEG Layer: " << layer << "\n";
328 os << "Has CRC: " << (f.crc?"Yes":"No") << "\n";
329 // TODO: Do something on VBR files, this is always 128 and has no meaning AFAIK
330 os << "Nominal bitrate: " << f.bitrate_ << " kbps\n";
331 os << "Sampling rate: " << f.freq << " Hz\n";
332 os << "Channel mode: " << chanmode << "\n";
333
334 return os;
335 }
336
337 } // namespace mp3
338
339 } // namespace mp3plot
340
341 // vim:set ts=4 et ai:
342
343