001: package com.quadcap.http.client;
002:
003: import java.io.BufferedOutputStream;
004: import java.io.ByteArrayOutputStream;
005: import java.io.FileInputStream;
006: import java.io.FileOutputStream;
007: import java.io.IOException;
008: import java.io.OutputStream;
009:
010: import com.quadcap.util.Debug;
011:
012: import com.quadcap.io.IO;
013:
014: /*
015: * Copyright 2004 by Stan Bailes and Quadcap Software.
016: *
017: **/
018: public class Mp3FrameStream extends OutputStream {
019: ByteArrayOutputStream frame = new ByteArrayOutputStream();
020: OutputStream frameData;
021: OutputStream nonFrameData;
022: int state = 0;
023:
024: int id = 0;
025: int layer = 0;
026: int protection = 0;
027: int bitrateEnc = 0;
028: int frequency = 0;
029: int pad = 0;
030: int priv = 0;
031: int mode = 0;
032: int modeExt = 0;
033: int copy = 0;
034: int home = 0;
035: int emph = 0;
036:
037: double bitrate = 0;
038: double samplerate = 0;
039: int framesize = 0;
040: int count = 0;
041:
042: int totalBytes = 0;
043: int nonFrameCount = 0;
044:
045: boolean started = false;
046: int bitrateEncStart = 0;
047: int frequencyStart = 0;
048:
049: public Mp3FrameStream() {
050: }
051:
052: public void init(OutputStream frameData, OutputStream nonFrameData) {
053: this .frameData = frameData;
054: this .nonFrameData = nonFrameData;
055: started = false;
056: }
057:
058: public void write(int b) throws IOException {
059: totalBytes++;
060: if (false && (totalBytes % 100000) == 99999) {
061: msg("totalBytes: " + totalBytes + ", state: " + state
062: + ", count: " + count + ", framesize: " + framesize);
063: }
064:
065: b &= 0xff;
066: switch (state) {
067: case 0:
068: if (b == 0xff) {
069: state = 1;
070: } else {
071: nonFrameData.write(b);
072: nonFrameCount++;
073: }
074: break;
075: case 1:
076: if ((b & 0xe0) == 0xe0) {
077: if (nonFrameCount > 0) {
078: msg("" + nonFrameCount + " non frame bytes @ "
079: + totalBytes);
080: }
081: nonFrameCount = 0;
082: //msg("Frame starts at byte " + ((totalBytes) - 2));
083: frame.reset();
084: frame.write(0xff);
085: frame.write(b);
086: id = (b & 0x8) >> 3;
087: layer = (b & 0x6) >> 1;
088: protection = (b & 0x1);
089: state = 2;
090: } else {
091: nonFrameData.write(0xff);
092: nonFrameData.write(b);
093: nonFrameCount++;
094: state = 0;
095: }
096: break;
097: case 2:
098: frame.write(b);
099: bitrateEnc = (b & 0xf0) >> 4;
100: frequency = (b & 0xc) >> 2;
101: pad = (b & 0x2) >> 1;
102: priv = (b & 0x1);
103: state = 3;
104: break;
105: case 3:
106: frame.write(b);
107: mode = (b & 0xc0) >> 6;
108: modeExt = (b & 0x3) >> 4;
109: copy = (b & 0x8) >> 3;
110: home = (b & 0x4) >> 2;
111: emph = (b & 0x3);
112: bitrate = bitrates[bitrateEnc][(id << 2) | layer] * 1000;
113: samplerate = samplerates[(id << 2) | frequency];
114: if (samplerate == 0) {
115: state = 0;
116: nonFrameData.write(frame.toByteArray());
117: nonFrameCount += 4;
118: } else if (started && bitrateEnc != bitrateEncStart
119: || frequency != frequencyStart) {
120: nonFrameData.write(frame.toByteArray());
121: nonFrameCount += 4;
122: state = 0;
123: } else {
124: framesize = (int) (144 * (bitrate / samplerate) + pad);
125: if (!started) {
126: msg("bitrate: " + bitrate + ", samplerate: "
127: + samplerate + ", framesize: " + framesize
128: + " brEnc: " + bitrateEnc + " id: " + id
129: + " layer: " + layer + " freq: "
130: + frequency);
131: started = true;
132: }
133: bitrateEncStart = bitrateEnc;
134: frequencyStart = frequency;
135: count = 4;
136: state = 4;
137: }
138: break;
139: case 4:
140: frame.write(b);
141: if (++count >= framesize) {
142: byte[] data = frame.toByteArray();
143: frameData.write(data);
144: state = 0;
145: }
146: break;
147: }
148: }
149:
150: int[] samplerates = { 22050, 24000, 16000, 0, // MPEG-2
151: 44100, 48000, 32000, 0 // MPEG-1
152: };
153:
154: int[][] bitrates = {
155: // MPEG-2 MPEG-1
156: // layer layer
157: // I II III I II III
158: { 0, 8, 32, 32, 0, 32, 32, 32 }, // 0
159: { 0, 8, 32, 32, 0, 32, 32, 32 }, // 1
160: { 0, 16, 48, 64, 0, 40, 48, 64 }, // 2
161: { 0, 24, 56, 96, 0, 48, 56, 96 }, // 3
162: { 0, 32, 64, 128, 0, 56, 64, 128 }, // 4
163: { 0, 64, 80, 160, 0, 64, 80, 160 }, // 5
164: { 0, 80, 96, 192, 0, 80, 96, 192 }, // 6
165: { 0, 96, 112, 224, 0, 96, 112, 224 }, // 7
166: { 0, 112, 128, 256, 0, 112, 128, 256 }, // 8
167: { 0, 128, 160, 288, 0, 128, 160, 288 }, // 9
168: { 0, 160, 192, 320, 0, 160, 192, 320 }, // 10
169: { 0, 112, 224, 352, 0, 192, 224, 352 }, // 11
170: { 0, 128, 256, 384, 0, 224, 256, 384 }, // 12
171: { 0, 256, 320, 416, 0, 256, 320, 416 }, // 13
172: { 0, 320, 384, 448, 0, 320, 384, 448 }, // 14
173: { 0, 320, 384, 448, 0, 320, 384, 448 } // 15
174: };
175:
176: void msg(String s) {
177: Debug.println(s);
178: }
179:
180: public static void main(String[] args) {
181: Mp3FrameStream mp = new Mp3FrameStream();
182: for (int i = 0; i < args.length; i++) {
183: String s = args[i];
184: try {
185: if (false) {
186: FileInputStream fi = new FileInputStream(s);
187: int b;
188: int state = 0;
189: int count = 0;
190: while ((b = fi.read()) >= 0) {
191: count++;
192: switch (state) {
193: case 0:
194: if (b == 0xff) {
195: state = 1;
196: }
197: break;
198: case 1:
199: if (b == 251) {
200: state = 2;
201: } else if (b != 0xff) {
202: state = 0;
203: }
204: break;
205: case 2:
206: if (b == 146) {
207: mp.msg("Sync at " + (count - 3));
208: }
209: state = 0;
210: break;
211: }
212: }
213: fi.close();
214: }
215: FileInputStream fis = new FileInputStream(s);
216: try {
217: FileOutputStream raw = new FileOutputStream(s
218: + ".out");
219: FileOutputStream skp = new FileOutputStream(s
220: + ".skp");
221: mp.init(raw, skp);
222: try {
223: IO.copyStream(fis, mp);
224: } finally {
225: mp.close();
226: }
227: } finally {
228: fis.close();
229: }
230: } catch (Throwable t) {
231: Debug.print(t);
232: }
233: }
234: }
235:
236: public void close() throws IOException {
237: Debug.println("close()");
238: frameData.close();
239: nonFrameData.close();
240: if (nonFrameCount > 0) {
241: Debug.println("" + nonFrameCount
242: + " trailing bytes no sync");
243: }
244: }
245: }
|