Coverage Report - org.jaudiotagger.audio.mp3.XingFrame
 
Classes in this File Line Coverage Branch Coverage Complexity
XingFrame
100%
61/61
83%
15/18
1.769
 
 1  
 package org.jaudiotagger.audio.mp3;
 2  
 
 3  
 import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
 4  
 
 5  
 import java.nio.ByteBuffer;
 6  
 import java.util.Arrays;
 7  
 
 8  
 /**
 9  
  * Xing Frame
 10  
  * <p/>
 11  
  * <p>In some MP3s which variable bit rate the first frame in the file contains a special frame called a Xing Frame,
 12  
  * instead of audio data. This is used to store additional information about the file. The most important aspect for
 13  
  * this library is details allowing us to determine the bitrate of a Variable Bit Rate VBR file without having
 14  
  * to process the whole file.
 15  
  * <p/>
 16  
  * Xing VBR Tag data format is 120 bytes long
 17  
  * 4 bytes for Header Tag
 18  
  * 4 bytes for Header Flags
 19  
  * 4 bytes for FRAME SIZE
 20  
  * 4 bytes for AUDIO_SIZE
 21  
  * 100 bytes for entry (NUMTOCENTRIES)
 22  
  * 4 bytes for VBR SCALE. a VBR quality indicator: 0=best 100=worst
 23  
  * <p/>
 24  
  * It my then contain a Lame Frame ( a Lame frame is in essence an extended Xing Frame
 25  
  */
 26  
 public class XingFrame
 27  
 {
 28  
 
 29  
     //The offset into first frame varies based on the MPEG frame properties
 30  
     private static final int MPEG_VERSION_1_MODE_MONO_OFFSET = 21;
 31  
     private static final int MPEG_VERSION_1_MODE_STEREO_OFFSET = 36;
 32  
     private static final int MPEG_VERSION_2_MODE_MONO_OFFSET = 13;
 33  
     private static final int MPEG_VERSION_2_MODE_STEREO_OFFSET = 21;
 34  
 
 35  
     private static final int XING_HEADER_BUFFER_SIZE = 120;
 36  
     private static final int XING_IDENTIFIER_BUFFER_SIZE = 4;
 37  
     private static final int XING_FLAG_BUFFER_SIZE = 4;
 38  
     private static final int XING_FRAMECOUNT_BUFFER_SIZE = 4;
 39  
     private static final int XING_AUDIOSIZE_BUFFER_SIZE = 4;
 40  
 
 41  
     public static final int MAX_BUFFER_SIZE_NEEDED_TO_READ_XING = MPEG_VERSION_1_MODE_STEREO_OFFSET + XING_HEADER_BUFFER_SIZE + LameFrame.LAME_HEADER_BUFFER_SIZE;
 42  
 
 43  
 
 44  
     private static final int BYTE_1 = 0;
 45  
     private static final int BYTE_2 = 1;
 46  
     private static final int BYTE_3 = 2;
 47  
     private static final int BYTE_4 = 3;
 48  
 
 49  
     /**
 50  
      * Use when it is a VBR (Variable Bitrate) file
 51  
      */
 52  4
     private static final byte[] XING_VBR_ID = {'X', 'i', 'n', 'g'};
 53  
 
 54  
     /**
 55  
      * Use when it is a CBR (Constant Bitrate) file
 56  
      */
 57  4
     private static final byte[] XING_CBR_ID = {'I', 'n', 'f', 'o'};
 58  
 
 59  
 
 60  
     private static ByteBuffer header;
 61  
 
 62  2023
     private boolean vbr = false;
 63  2023
     private boolean isFrameCountEnabled = false;
 64  2023
     private int frameCount = -1;
 65  2023
     private boolean isAudioSizeEnabled = false;
 66  2023
     private int audioSize = -1;
 67  
     private LameFrame lameFrame;
 68  
 
 69  
     /**
 70  
      * Read the Xing Properties from the buffer
 71  
      */
 72  
     private XingFrame()
 73  2023
     {
 74  
         //Go to start of Buffer
 75  2023
         header.rewind();
 76  
 
 77  
         //Set Vbr
 78  2023
         setVbr();
 79  
 
 80  
         //Read Flags, only the fourth byte of interest to us
 81  2023
         byte flagBuffer[] = new byte[XING_FLAG_BUFFER_SIZE];
 82  2023
         header.get(flagBuffer);
 83  
 
 84  
         //Read FrameCount if flag set
 85  2023
         if ((flagBuffer[BYTE_4] & (byte) (1)) != 0)
 86  
         {
 87  2023
             setFrameCount();
 88  
         }
 89  
 
 90  
         //Read Size if flag set
 91  2023
         if ((flagBuffer[BYTE_4] & (byte) (1 << 1)) != 0)
 92  
         {
 93  2023
             setAudioSize();
 94  
         }
 95  
 
 96  
         //TODO TOC
 97  
         //TODO VBR Quality
 98  
 
 99  
         //Look for LAME Header as long as we have enough bytes to do it properly
 100  2023
         if (header.limit() >= XING_HEADER_BUFFER_SIZE + LameFrame.LAME_HEADER_BUFFER_SIZE)
 101  
         {
 102  2023
             header.position(XING_HEADER_BUFFER_SIZE);
 103  2023
             lameFrame = LameFrame.parseLameFrame(header);
 104  
         }
 105  2023
     }
 106  
 
 107  
     public LameFrame getLameFrame()
 108  
     {
 109  4044
         return lameFrame;
 110  
     }
 111  
 
 112  
     /**
 113  
      * Set whether or not VBR, (Xing can also be used for CBR though this is less useful)
 114  
      */
 115  
     private void setVbr()
 116  
     {
 117  
         //Is it VBR or CBR
 118  2023
         byte[] identifier = new byte[XING_IDENTIFIER_BUFFER_SIZE];
 119  2023
         header.get(identifier);
 120  2023
         if (Arrays.equals(identifier, XING_VBR_ID))
 121  
         {
 122  33
             MP3File.logger.finest("Is Vbr");
 123  33
             vbr = true;
 124  
         }
 125  2023
     }
 126  
 
 127  
     /**
 128  
      * Set count of frames
 129  
      */
 130  
     private void setFrameCount()
 131  
     {
 132  2023
         byte frameCountBuffer[] = new byte[XING_FRAMECOUNT_BUFFER_SIZE];
 133  2023
         header.get(frameCountBuffer);
 134  2023
         isFrameCountEnabled = true;
 135  2023
         frameCount = (frameCountBuffer[BYTE_1] << 24) & 0xFF000000 | (frameCountBuffer[BYTE_2] << 16) & 0x00FF0000 | (frameCountBuffer[BYTE_3] << 8) & 0x0000FF00 | frameCountBuffer[BYTE_4] & 0x000000FF;
 136  2023
     }
 137  
 
 138  
     /**
 139  
      * @return true if frameCount has been specified in header
 140  
      */
 141  
     public final boolean isFrameCountEnabled()
 142  
     {
 143  2023
         return isFrameCountEnabled;
 144  
     }
 145  
 
 146  
     /**
 147  
      * @return count of frames
 148  
      */
 149  
     public final int getFrameCount()
 150  
     {
 151  2023
         return frameCount;
 152  
     }
 153  
 
 154  
     /**
 155  
      * Set size of AudioData
 156  
      */
 157  
     private void setAudioSize()
 158  
     {
 159  2023
         byte frameSizeBuffer[] = new byte[XING_AUDIOSIZE_BUFFER_SIZE];
 160  2023
         header.get(frameSizeBuffer);
 161  2023
         isAudioSizeEnabled = true;
 162  2023
         audioSize = (frameSizeBuffer[BYTE_1] << 24) & 0xFF000000 | (frameSizeBuffer[BYTE_2] << 16) & 0x00FF0000 | (frameSizeBuffer[BYTE_3] << 8) & 0x0000FF00 | frameSizeBuffer[BYTE_4] & 0x000000FF;
 163  2023
     }
 164  
 
 165  
     /**
 166  
      * @return true if audioSize has been specified in header
 167  
      */
 168  
     public final boolean isAudioSizeEnabled()
 169  
     {
 170  33
         return isAudioSizeEnabled;
 171  
     }
 172  
 
 173  
     /**
 174  
      * @return size of audio data in bytes
 175  
      */
 176  
     public final int getAudioSize()
 177  
     {
 178  66
         return audioSize;
 179  
     }
 180  
 
 181  
     /**
 182  
      * Parse the XingFrame of an MP3File, cannot be called until we have validated that
 183  
      * this is a XingFrame
 184  
      *
 185  
      * @return
 186  
      * @throws InvalidAudioFrameException
 187  
      */
 188  
     public static XingFrame parseXingFrame() throws InvalidAudioFrameException
 189  
     {
 190  2023
         XingFrame xingFrame = new XingFrame();
 191  2023
         return xingFrame;
 192  
     }
 193  
 
 194  
     /**
 195  
      * IS this a Xing frame
 196  
      *
 197  
      * @param bb
 198  
      * @param mpegFrameHeader
 199  
      * @return true if this is a Xing frame
 200  
      */
 201  
     public static boolean isXingFrame(ByteBuffer bb, MPEGFrameHeader mpegFrameHeader)
 202  
     {
 203  
         //We store this so can return here after scanning through buffer
 204  2202
         int startPosition = bb.position();
 205  
 
 206  
         //Get to Start of where Xing Frame Should be ( we dont know if it is one at this point)
 207  2202
         if (mpegFrameHeader.getVersion() == MPEGFrameHeader.VERSION_1)
 208  
         {
 209  2130
             if (mpegFrameHeader.getChannelMode() == MPEGFrameHeader.MODE_MONO)
 210  
             {
 211  2025
                 bb.position(startPosition + MPEG_VERSION_1_MODE_MONO_OFFSET);
 212  
             }
 213  
             else
 214  
             {
 215  105
                 bb.position(startPosition + MPEG_VERSION_1_MODE_STEREO_OFFSET);
 216  
             }
 217  
         }
 218  
         //MPEGVersion 2 and 2.5
 219  
         else
 220  
         {
 221  72
             if (mpegFrameHeader.getChannelMode() == MPEGFrameHeader.MODE_MONO)
 222  
             {
 223  60
                 bb.position(startPosition + MPEG_VERSION_2_MODE_MONO_OFFSET);
 224  
             }
 225  
             else
 226  
             {
 227  12
                 bb.position(startPosition + MPEG_VERSION_2_MODE_STEREO_OFFSET);
 228  
             }
 229  
         }
 230  
 
 231  
         //Create header from here
 232  2202
         header = bb.slice();
 233  
 
 234  
         // Return Buffer to start Point
 235  2202
         bb.position(startPosition);
 236  
 
 237  
         //Check Identifier
 238  2202
         byte[] identifier = new byte[XING_IDENTIFIER_BUFFER_SIZE];
 239  2202
         header.get(identifier);
 240  2202
         if ((!Arrays.equals(identifier, XING_VBR_ID)) && (!Arrays.equals(identifier, XING_CBR_ID)))
 241  
         {
 242  179
             return false;
 243  
         }
 244  2023
         MP3File.logger.finest("Found Xing Frame");
 245  2023
         return true;
 246  
     }
 247  
 
 248  
     /**
 249  
      * Is this XingFrame detailing a varaible bit rate MPEG
 250  
      *
 251  
      * @return
 252  
      */
 253  
     public final boolean isVbr()
 254  
     {
 255  2095
         return vbr;
 256  
     }
 257  
 
 258  
     /**
 259  
      * @return a string represntation
 260  
      */
 261  
     public String toString()
 262  
     {
 263  80
         return "xingheader" + " vbr:" + vbr + " frameCountEnabled:" + isFrameCountEnabled + " frameCount:" + frameCount + " audioSizeEnabled:" + isAudioSizeEnabled + " audioFileSize:" + audioSize;
 264  
     }
 265  
 }