FileDocCategorySizeDatePackage
VbriFrame.javaAPI DocJaudiotagger 2.0.46463Mon Sep 12 16:08:14 BST 2011org.jaudiotagger.audio.mp3

VbriFrame.java

package org.jaudiotagger.audio.mp3;

import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;

import java.nio.ByteBuffer;
import java.util.Arrays;

/**
 * Vrbi Frame
 * <p/>
 * <p>In MP3s encoded using the franhofer encoder which variable bit rate the first frame in the file contains a
 * special frame called a Vrbi Frame, instead of audio data (Other Vbr encoders use the more common Xing Frame).
 * This is used to store additional information about the file. The most important aspect for
 * this library is details allowing us to determine the bitrate of a Variable Bit Rate VBR file without having
 * to process the whole file.
 * <p/>
 * From http://www.codeproject.com/KB/audio-video/mpegaudioinfo.aspx#SideInfo
 * <p/>
 * This header is only used by MPEG audio files encoded with the Fraunhofer Encoder as far as I know. It is different from the XING header. You find it exactly
 * 32 bytes after the end of the first MPEG audio header in the file. (Note that the position is zero-based; position, length and example are each in byte-format.)
 * Position 	Length 	Meaning 	Example
 * 0    		    4 	VBR header ID in 4 ASCII chars, always 'VBRI', not NULL-terminated 	'VBRI'
 * 4	  			2 	Version ID as Big-Endian WORD 	1
 * 6 				2 	Delay as Big-Endian float 	7344
 * 8 				2 	Quality indicator 	75
 * 10 				4 	Number of Bytes of Audio as Big-Endian DWORD 	45000
 * 14 				4 	Number of Frames as Big-Endian DWORD 	7344
 * 18 				2 	Number of entries within TOC table as Big-Endian WORD 	100
 * 20 				2 	Scale factor of TOC table entries as Big-Endian DWORD 	1
 * 22 				2 	Size per table entry in bytes (max 4) as Big-Endian WORD 	2
 * 24 				2 	Frames per table entry as Big-Endian WORD 	845
 * 26 						TOC entries for seeking as Big-Endian integral. From size per table entry and number of entries, you can calculate the length of this field.
 * <p/>
 */
public class VbriFrame
{

    //The offset into frame
    private static final int VBRI_OFFSET = MPEGFrameHeader.HEADER_SIZE + 32;

    private static final int VBRI_HEADER_BUFFER_SIZE = 120; //TODO this is just a guess, not right
    private static final int VBRI_IDENTIFIER_BUFFER_SIZE = 4;
    private static final int VBRI_DELAY_BUFFER_SIZE = 2;
    private static final int VBRI_QUALITY_BUFFER_SIZE = 2;
    private static final int VBRI_AUDIOSIZE_BUFFER_SIZE = 4;
    private static final int VBRI_FRAMECOUNT_BUFFER_SIZE = 4;
    private static final int VBRI_TOC_ENTRY_BUFFER_SIZE = 2;

    public static final int MAX_BUFFER_SIZE_NEEDED_TO_READ_VBRI = VBRI_OFFSET + VBRI_HEADER_BUFFER_SIZE;

    private static final int BYTE_1 = 0;
    private static final int BYTE_2 = 1;
    private static final int BYTE_3 = 2;
    private static final int BYTE_4 = 3;

    /**
     * Identifier
     */
    private static final byte[] VBRI_VBR_ID = {'V', 'B', 'R', 'I'};

    private ByteBuffer header;

    private boolean vbr = false;
    private int frameCount = -1;
    private int audioSize = -1;
    private LameFrame lameFrame;

    /**
     * Read the VBRI Properties from the buffer
     */
    private VbriFrame(ByteBuffer header)
    {
        this.header=header;
        //Go to start of Buffer
        header.rewind();
        header.position(10);
        setAudioSize();
        setFrameCount();
    }

    /**
     * Set size of AudioData
     */
    private void setAudioSize()
    {
        byte frameSizeBuffer[] = new byte[VBRI_AUDIOSIZE_BUFFER_SIZE];
        header.get(frameSizeBuffer);
        boolean audioSizeEnabled = true;
        audioSize = (frameSizeBuffer[BYTE_1] << 24) & 0xFF000000 | (frameSizeBuffer[BYTE_2] << 16) & 0x00FF0000 | (frameSizeBuffer[BYTE_3] << 8) & 0x0000FF00 | frameSizeBuffer[BYTE_4] & 0x000000FF;
    }

    /**
     * Set count of frames
     */
    private void setFrameCount()
    {
        byte frameCountBuffer[] = new byte[VBRI_FRAMECOUNT_BUFFER_SIZE];
        header.get(frameCountBuffer);
        boolean frameCountEnabled = true;
        frameCount = (frameCountBuffer[BYTE_1] << 24) & 0xFF000000 | (frameCountBuffer[BYTE_2] << 16) & 0x00FF0000 | (frameCountBuffer[BYTE_3] << 8) & 0x0000FF00 | frameCountBuffer[BYTE_4] & 0x000000FF;
    }


    /**
     * @return count of frames
     */
    public final int getFrameCount()
    {
        return frameCount;
    }

    /**
     * @return size of audio data in bytes
     */
    public final int getAudioSize()
    {
        return audioSize;
    }

    /**
     * Parse the VBRIFrame of an MP3File, cannot be called until we have validated that
     * this is a VBRIFrame
     *
     * @return
     * @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
     *
     */
    public static VbriFrame parseVBRIFrame(ByteBuffer header) throws InvalidAudioFrameException
    {
        VbriFrame VBRIFrame = new VbriFrame(header);
        return VBRIFrame;
    }

    /**
     * IS this a VBRI frame
     *
     * @param bb
     * @param mpegFrameHeader
     * @return raw header if this is a VBRI frame
     */
    public static ByteBuffer isVbriFrame(ByteBuffer bb, MPEGFrameHeader mpegFrameHeader)
    {

        //We store this so can return here after scanning through buffer
        int startPosition = bb.position();
        MP3File.logger.finest("Checking VBRI Frame at" + startPosition);

        bb.position(startPosition + VBRI_OFFSET);

        //Create header from here
        ByteBuffer header = bb.slice();

        // Return Buffer to start Point
        bb.position(startPosition);

        //Check Identifier
        byte[] identifier = new byte[VBRI_IDENTIFIER_BUFFER_SIZE];
        header.get(identifier);
        if ((!Arrays.equals(identifier, VBRI_VBR_ID)))
        {
            return null;
        }
        MP3File.logger.finest("Found VBRI Frame");
        return header;
    }

    /**
     * Is this VBRIFrame detailing a varaible bit rate MPEG
     *
     * @return
     */
    public final boolean isVbr()
    {
        return true;
    }

    public String getEncoder()
    {
        return "Fraunhofer";
    }

    /**
     * @return a string represntation
     */
    public String toString()
    {
        return "VBRIheader" + " vbr:" + vbr + " frameCount:" + frameCount + " audioFileSize:" + audioSize + " encoder:" + getEncoder();
    }
}