FileDocCategorySizeDatePackage
Mp4DataBox.javaAPI DocJaudiotagger 2.0.45623Tue Aug 02 15:48:22 BST 2011org.jaudiotagger.tag.mp4.atom

Mp4DataBox.java

package org.jaudiotagger.tag.mp4.atom;

import org.jaudiotagger.audio.generic.Utils;
import org.jaudiotagger.audio.mp4.atom.AbstractMp4Box;
import org.jaudiotagger.audio.mp4.atom.Mp4BoxHeader;
import org.jaudiotagger.tag.mp4.field.Mp4FieldType;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

/**
 * This box is used within both normal metadat boxes and ---- boxes to hold the actual data.
 * <p/>
 * <p>Format is as follows:
 * :length          (4 bytes)
 * :name 'Data'     (4 bytes)
 * :atom version    (1 byte)
 * :atom type flags (3 bytes)
 * :locale field    (4 bytes) //Currently always zero
 * :data
 */
public class Mp4DataBox extends AbstractMp4Box
{
    public static final String IDENTIFIER = "data";

    public static final int VERSION_LENGTH = 1;
    public static final int TYPE_LENGTH = 3;
    public static final int NULL_LENGTH = 4;
    public static final int PRE_DATA_LENGTH = VERSION_LENGTH + TYPE_LENGTH + NULL_LENGTH;
    public static final int DATA_HEADER_LENGTH = Mp4BoxHeader.HEADER_LENGTH + PRE_DATA_LENGTH;

    public static final int TYPE_POS = VERSION_LENGTH;

    //For use externally
    public static final int TYPE_POS_INCLUDING_HEADER = Mp4BoxHeader.HEADER_LENGTH + TYPE_POS;

    private int type;
    private String content;

    public static final int NUMBER_LENGTH = 2;

    //Holds the numbers decoded
    private List<Short> numbers;

    //Holds bytedata for byte fields as is not clear for multibyte fields exactly these should be wrttien
    private byte[] bytedata;

    /**
     * @param header     parentHeader info
     * @param dataBuffer data of box (doesnt include parentHeader data)
     */
    public Mp4DataBox(Mp4BoxHeader header, ByteBuffer dataBuffer)
    {
        this.header = header;
        //Double check
        if (!header.getId().equals(IDENTIFIER))
        {
            throw new RuntimeException("Unable to process data box because identifier is:" + header.getId());
        }

        //Make slice so operations here don't effect position of main buffer
        this.dataBuffer = dataBuffer.slice();

        //Type
        type = Utils.getIntBE(this.dataBuffer, Mp4DataBox.TYPE_POS, Mp4DataBox.TYPE_POS + Mp4DataBox.TYPE_LENGTH - 1);

        if (type == Mp4FieldType.TEXT.getFileClassId())
        {
            content = Utils.getString(this.dataBuffer, PRE_DATA_LENGTH, header.getDataLength() - PRE_DATA_LENGTH, header.getEncoding());
        }
        else if (type == Mp4FieldType.IMPLICIT.getFileClassId())
        {
            numbers = new ArrayList<Short>();

            for (int i = 0; i < ((header.getDataLength() - PRE_DATA_LENGTH) / NUMBER_LENGTH); i++)
            {
                short number = Utils.getShortBE(this.dataBuffer, PRE_DATA_LENGTH + (i * NUMBER_LENGTH), PRE_DATA_LENGTH + (i * NUMBER_LENGTH) + (NUMBER_LENGTH - 1));
                numbers.add(number);
            }

            //Make String representation  (separate values with slash)
            StringBuffer sb = new StringBuffer();
            ListIterator<Short> iterator = numbers.listIterator();
            while (iterator.hasNext())
            {
                sb.append(iterator.next());
                if (iterator.hasNext())
                {
                    sb.append("/");
                }
            }
            content = sb.toString();
        }
        else if (type == Mp4FieldType.INTEGER.getFileClassId())
        {
            //TODO byte data length seems to be 1 for pgap and cpil but 2 for tmpo ?
            //Create String representation for display
            content = Utils.getIntBE(this.dataBuffer, PRE_DATA_LENGTH, header.getDataLength() - 1) + "";

            //But store data for safer writing back to file
            bytedata = new byte[header.getDataLength() - PRE_DATA_LENGTH];
            int pos = dataBuffer.position();
            dataBuffer.position(pos + PRE_DATA_LENGTH);
            dataBuffer.get(bytedata);
            dataBuffer.position(pos);

            //Songbird uses this type for trkn atom (normally implicit type) is used so just added this code so can be used
            //by the Mp4TrackField atom
            numbers = new ArrayList<Short>();
            for (int i = 0; i < ((header.getDataLength() - PRE_DATA_LENGTH) / NUMBER_LENGTH); i++)
            {
                short number = Utils.getShortBE(this.dataBuffer, PRE_DATA_LENGTH + (i * NUMBER_LENGTH), PRE_DATA_LENGTH + (i * NUMBER_LENGTH) + (NUMBER_LENGTH - 1));
                numbers.add(number);
            }

        }
        else if (type == Mp4FieldType.COVERART_JPEG.getFileClassId())
        {
            content = Utils.getString(this.dataBuffer, PRE_DATA_LENGTH, header.getDataLength() - PRE_DATA_LENGTH, header.getEncoding());
        }
    }

    public String getContent()
    {
        return content;
    }

    public int getType()
    {

        return type;
    }

    /**
     * Return numbers, only valid for numeric fields
     *
     * @return numbers
     */
    //TODO this is only applicable for numeric databoxes, should we subclass dont know type until start
    //constructing and we also have Mp4tagTextNumericField class as well
    public List<Short> getNumbers()
    {
        return numbers;
    }

    /**
     * Return raw byte data only vaid for byte fields
     *
     * @return byte data
     */
    public byte[] getByteData()
    {
        return bytedata;
    }
}