KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > media > format > audio > mpeg > MpegAudioHeader


1 /*
2  * JBoss, the OpenSource J2EE webOS
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7
8 package org.jboss.media.format.audio.mpeg;
9
10 import java.io.IOException JavaDoc;
11 import java.io.InputStream JavaDoc;
12 import java.io.PushbackInputStream JavaDoc;
13 import java.util.HashMap JavaDoc;
14 import java.util.Map JavaDoc;
15
16 import javax.emb.FormatSyntaxException;
17 import javax.emb.MediaException;
18 import javax.emb.MediaHeader;
19
20 /**
21  * Represents an MPEG Audio Header
22  *
23  * @version <tt>$Revision 1.1 $</tt>
24  * @author <a HREF="mailto:ogreen@users.sourceforge.net">Owen Green</a>
25  */

26 public class MpegAudioHeader implements MediaHeader
27 {
28    /*
29     * Lookups for bitrates at various combinations of MPEG version and layer
30     */

31    //Version 1, Layer I
32
private final static int[] v1L1BitRates =
33       { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 };
34
35    //Version 1, Layer II
36
private final static int[] v1L2BitRates =
37       { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 };
38
39    //Version 1, Layer III
40
private final static int[] v1L3BitRates =
41       { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };
42
43    //Version 2 and 2.5, Layer I
44
private final static int[] v2L1BitRates =
45       { 0, 32, 48, 56, 64, 80, 96, 112, 128, 411, 460, 476, 192, 224, 256 };
46
47    //Version 2 and 2.5, Layer II and III
48
private final static int[] v2L23BitRates =
49       { 0, 8, 16, 24, 32, 40, 28, 56, 64, 80, 96, 112, 128, 144, 160 };
50
51    /*
52     * Lookups for sampling rates for each version
53     */

54    private final static int[] v1SampleRates = { 44100, 48000, 32000 };
55    private final static int[] v2SampleRates = { 22050, 24000, 16000 };
56    private final static int[] v25SampleRates = { 11025, 12000, 8000 };
57
58    /*
59     * field name keys for fields map
60     */

61    private final static String JavaDoc VERSION_KEY = "version";
62    private final static String JavaDoc LAYER_KEY = "layer";
63    private final static String JavaDoc BITRATE_KEY = "bitRate";
64    private final static String JavaDoc SAMPLERATE_KEY = "samplingRate";
65    private final static String JavaDoc CHANNELMODE_KEY = "channelMode";
66    private final static String JavaDoc COPYRIGHT_KEY = "copyright";
67    private final static String JavaDoc ORGINAL_KEY = "original";
68
69    //Map to hold fields in
70
private final Map JavaDoc fieldMap = new HashMap JavaDoc(7);
71
72    private final PushbackInputStream JavaDoc content;
73
74    /**
75     * Constructs a new instance and attempts to extract an MPEG audio header
76     * from <code>mediaObject</code>
77     *
78     * @param mediaObject the media to extract an MPEG Audio header from
79     * @throws MediaException if an there is an error accessing the media
80     * content
81     * @throws FormatSyntaxException if invalid data for this format is
82     * encoutered
83     * @throws RemoteException if an RMI error occurs
84     */

85    public MpegAudioHeader(InputStream JavaDoc data)
86       throws MediaException, FormatSyntaxException
87    {
88       try
89       {
90          content = new PushbackInputStream JavaDoc(data, 4);
91          int headerPos = findHeaderStart(content, 0);
92
93          if (headerPos != -1)
94          {
95             byte[] header = new byte[4];
96             content.read(header);
97             readHeader(header);
98          }
99          else
100          {
101             throw new FormatSyntaxException("MPEG Audio header could not be found; this is not valid MPEG Audio data");
102          }
103       }
104       catch (IOException JavaDoc ex)
105       {
106          throw new MediaException(ex.getMessage());
107       }
108    }
109
110    /**
111     * Returns the bit rate for this media in kbps.
112     *
113     * @return int the bit rate for this media
114     */

115    public int getBitrate()
116    {
117       return ((Integer JavaDoc) fieldMap.get(BITRATE_KEY)).intValue();
118    }
119
120    /**
121     * Returns the channel mode for this MPEG audio.
122     *
123     * @return MpegAudioFormat.ChannelMode the channel mode for this MPEG audio
124     * @see MpegAudioFormat.ChannelMode
125     */

126    public MpegAudioFormat.ChannelMode getChannelMode()
127    {
128       return (MpegAudioFormat.ChannelMode) fieldMap.get(CHANNELMODE_KEY);
129    }
130
131    /**
132     * Indicates whether this is copyrighted material or not
133     *
134     * @return boolean <code>true</code> if this is copyrighted material
135     */

136    public boolean isCopyright()
137    {
138       return ((Boolean JavaDoc) fieldMap.get(COPYRIGHT_KEY)).booleanValue();
139    }
140
141    /**
142     * Returns the MPEG layer of this media
143     *
144     * @return MpegAudioFormat.Layer the MPEG audio layer of this media
145     * @see MpegAudioFormat
146     * @see MpegAudioFormat.Layer
147     */

148    public MpegAudioFormat.Layer getLayer()
149    {
150       return (MpegAudioFormat.Layer) fieldMap.get(LAYER_KEY);
151    }
152
153    /**
154     * Indicates whether this is the orginal media, or a copy
155     *
156     * @return boolean <code>true</code> if this is the orginal media
157     */

158    public boolean isOriginal()
159    {
160       return ((Boolean JavaDoc) fieldMap.get(ORGINAL_KEY)).booleanValue();
161    }
162
163    /**
164     * Returns the sampling rate of this audio in Hz
165     *
166     * @return int the sampling rate of this audio
167     */

168    public int getSamplerate()
169    {
170       return ((Integer JavaDoc) fieldMap.get(SAMPLERATE_KEY)).intValue();
171    }
172
173    /**
174     * Returns the MPEG version of this media
175     *
176     * @return MpegAudioFormat.Version the MPEG version of this media
177     * @see MpegAudioFormat.Version
178     */

179    public MpegAudioFormat.Version getVersion()
180    {
181       return (MpegAudioFormat.Version) fieldMap.get(VERSION_KEY);
182    }
183
184    /**
185     * Provides a list of the field names in this header
186     *
187     * @return a list of the field names in this header
188     * @see javax.emb.MediaHeader#getFieldNames()
189     */

190    public String JavaDoc[] getFieldNames()
191    {
192       return (String JavaDoc[]) fieldMap.keySet().toArray(new String JavaDoc[0]);
193    }
194
195    /**
196     * Retreives a field by name. In the case of primitive fields, the object
197     * equivalent is returned (e.g. <code>int</code> becomes <code>java.lang.Integer</code>
198     *
199     * @param fieldname the name of the field to access
200     * @return the requested field, or <code>null</code> if the field name
201     * doesn't exist
202     * @see javax.emb.MediaHeader#getField(String)
203     */

204    public Object JavaDoc getField(String JavaDoc fieldname)
205    {
206       return fieldMap.get(fieldname);
207    }
208
209    /**
210     * Finds the start of the next MPEG audio header after <code>offset</code>,
211     * skipping any ID3 tag.
212     *
213     * @param mediaObject the {@link javax.emb.Media}to search for a header
214     * @param the offset to start searching at
215     * @return the position in the <code>Media</code> of the next header, or
216     * <code>-1</code> if no header is found
217     */

218    private int findHeaderStart(PushbackInputStream JavaDoc content, int offset)
219       throws MediaException
220    {
221       try
222       {
223          if (offset == 0)
224          {
225             byte[] id3 = new byte[4];
226             content.read(id3);
227             String JavaDoc id3Test = new String JavaDoc(id3);
228             content.unread(id3);
229             //see if we've got an ID3 tag at the begining
230
if (id3Test.equals("ID3"))
231             {
232                ID3Tag id3Tag = new ID3Tag(content);
233                offset += id3Tag.getSize();
234             }
235          }
236
237          byte[] headerTest = new byte[2];
238
239          while ((content.read(headerTest)) != -1)
240          {
241             //check for frame sync block - 1111 1111 111
242
if (((headerTest[0] & 0xFF) == 0xFF)
243                && ((headerTest[1] & 0xE0) == 0xE0))
244             {
245                content.unread(headerTest);
246                return offset;
247             }
248             content.unread(headerTest[1]);
249             offset++;
250          }
251       }
252       catch (IOException JavaDoc e)
253       {
254          throw new MediaException(e);
255       }
256
257       return -1;
258    }
259
260    /**
261     * Reads and interprets an MPEG audio header chunk
262     *
263     * @param header the header data - at least 4 bytes long
264     * @throws FormatSyntaxException if illegal data is encountered
265     * @throws IllegalArgumentException if <code>header</code> is less than 4
266     * elements long
267     */

268    private void readHeader(byte[] header) throws FormatSyntaxException
269    {
270       if (header.length < 4)
271       {
272          throw new IllegalArgumentException JavaDoc("Not enough header data");
273       }
274
275       fieldMap.put(VERSION_KEY, getVersion(header[1]));
276       fieldMap.put(LAYER_KEY, getLayer(header[1]));
277       fieldMap.put(BITRATE_KEY, new Integer JavaDoc(getBitrate(header[2])));
278       fieldMap.put(SAMPLERATE_KEY, new Integer JavaDoc(getSampleRate(header[2])));
279       fieldMap.put(CHANNELMODE_KEY, getChannelMode(header[3]));
280       fieldMap.put(COPYRIGHT_KEY, new Boolean JavaDoc(getCopyright(header[3])));
281       fieldMap.put(ORGINAL_KEY, new Boolean JavaDoc(getOriginal(header[3])));
282    }
283
284    /**
285     * Extracts the MPEG Version from an MPEG header
286     *
287     * @return the MPEG version
288     * @param versionByte the byte of the header that contains the version
289     * information
290     * @throws FormatSyntaxException if an illegal version ID is encountered
291     * @see MpegAudioFormat.Version
292     */

293    private MpegAudioFormat.Version getVersion(byte versionByte)
294       throws FormatSyntaxException
295    {
296       switch (versionByte & 0x18) //mask with 00011000
297
{
298          case 0x00 :
299             return MpegAudioFormat.Version.MPEG25;
300          case 0x10 :
301             return MpegAudioFormat.Version.MPEG2;
302          case 0x18 :
303             return MpegAudioFormat.Version.MPEG1;
304          default :
305             throw new FormatSyntaxException("Could not determine MPEG Audio version");
306       }
307    }
308
309    /**
310     * Extracts the MPEG Layer from an MPEG header byte
311     *
312     * @return the MPEG Audio Layer
313     * @param layerByte the byte that contains layer information
314     * @throws FormatSyntaxException if an illegal Layer value is encountered
315     * @see MpegAudioFormat.Layer
316     */

317    private MpegAudioFormat.Layer getLayer(byte layerByte)
318       throws FormatSyntaxException
319    {
320       switch (layerByte & 0x06) //mask with 0000 0110
321
{
322          case 0x02 :
323             return MpegAudioFormat.Layer.LAYERIII;
324          case 0x04 :
325             return MpegAudioFormat.Layer.LAYERII;
326          case 0x06 :
327             return MpegAudioFormat.Layer.LAYERI;
328          default :
329             throw new FormatSyntaxException("Could not determine MPEG Audio version");
330       }
331    }
332
333    /**
334     * Estabilshes whether this MPEG frame has a CRC checksum after the header
335     * (not currently used)
336     *
337     * @param crcByte the header byte containing the CRC indicator bit
338     */

339    private boolean getCRC(byte crcByte)
340    {
341       return (crcByte & 0x1) == 0;
342    }
343
344    /**
345     * Extracts the MPEG bit rate from an MPEG header byte
346     *
347     * @param bitrateByte the header byte containing bit rate information
348     * @throws FormatSyntaxException if an illegal bit rate value is encountered
349     * @throws IllegalStateException if the Version and Layer have not yet been
350     * extracted
351     */

352    private int getBitrate(byte bitrateByte) throws FormatSyntaxException
353    {
354       int bitrateKey = (bitrateByte & 0xF0) >>> 4;
355
356       if (0xF == bitrateKey)
357       {
358          throw new FormatSyntaxException("Illegal bitrate specified");
359       }
360
361       if (MpegAudioFormat.Version.MPEG1 == getVersion())
362       {
363          if (MpegAudioFormat.Layer.LAYERI == getLayer())
364          {
365             return v1L1BitRates[bitrateKey];
366          }
367
368          if (MpegAudioFormat.Layer.LAYERII == getLayer())
369          {
370             return v1L2BitRates[bitrateKey];
371          }
372
373          if (MpegAudioFormat.Layer.LAYERIII == getLayer())
374          {
375             return v1L3BitRates[bitrateKey];
376          }
377       }
378
379       if ((MpegAudioFormat.Version.MPEG2 == getVersion())
380          || (MpegAudioFormat.Version.MPEG25 == getVersion()))
381       {
382          if (MpegAudioFormat.Layer.LAYERI == getLayer())
383          {
384             return v2L1BitRates[bitrateKey];
385          }
386
387          if ((MpegAudioFormat.Layer.LAYERII == getLayer())
388             || (MpegAudioFormat.Layer.LAYERIII == getLayer()))
389          {
390             return v2L23BitRates[bitrateKey];
391          }
392       }
393
394       throw new IllegalStateException JavaDoc("Version and layer must be determined before extracting bitrate");
395    }
396
397    /**
398     * Extracts the MPEG sample rate from an MPEG header byte
399     *
400     * @param samplerateByte the header byte containing sample rate information
401     * @throws FormatSyntaxException if an illegal sample rate value is
402     * encountered
403     * @throws IllegalStateException if the Version and Layer have not yet been
404     * extracted
405     */

406    private int getSampleRate(byte samplerateByte) throws FormatSyntaxException
407    {
408       int samplerateKey = (samplerateByte & 0xC) >>> 2;
409
410       if (samplerateKey > 2)
411       {
412          throw new FormatSyntaxException("Illegal sampling rate specified");
413       }
414
415       if (MpegAudioFormat.Version.MPEG1 == getVersion())
416       {
417          return v1SampleRates[samplerateKey];
418       }
419
420       if (MpegAudioFormat.Version.MPEG2 == getVersion())
421       {
422          return v2SampleRates[samplerateKey];
423       }
424
425       if (MpegAudioFormat.Version.MPEG25 == getVersion())
426       {
427          return v25SampleRates[samplerateKey];
428       }
429
430       throw new IllegalStateException JavaDoc("Version must be determined before extracting sample rate");
431    }
432
433    /**
434     * Extracts the MPEG channel mode from a MPEG header byte
435     *
436     * @param channelModeByte the byte containing channel mode information
437     * @throws FormatSyntaxException if an illegal channel mode value is
438     * encountered
439     */

440    private MpegAudioFormat.ChannelMode getChannelMode(byte channelModeByte)
441       throws FormatSyntaxException
442    {
443       switch (channelModeByte & 0xC0)
444       {
445          case 0x00 :
446             return MpegAudioFormat.ChannelMode.STEREO;
447          case 0x40 :
448             return MpegAudioFormat.ChannelMode.JOINT_STEREO;
449          case 0x80 :
450             return MpegAudioFormat.ChannelMode.DUAL_CHANNEL;
451          case 0xC0 :
452             return MpegAudioFormat.ChannelMode.SINGLE_CHANNEL;
453          default :
454             throw new FormatSyntaxException("Illegal Channel Mode encountered");
455       }
456    }
457
458    /**
459     * Extracts the copyright bit from an MPEG header byte
460     */

461    private boolean getCopyright(byte copyrightByte)
462    {
463       return (copyrightByte & 8) != 0;
464    }
465
466    private boolean getOriginal(byte originalByte)
467    {
468       return (originalByte & 4) != 0;
469    }
470 }
Popular Tags