FileDocCategorySizeDatePackage
ID3v24Frame.javaAPI DocJaudiotagger 2.0.442917Wed Jun 08 12:05:40 BST 2011org.jaudiotagger.tag.id3

ID3v24Frame

public class ID3v24Frame extends AbstractID3v2Frame
Represents an ID3v2.4 frame.
author
: Paul Taylor
author
: Eric Farng
version
$Id: ID3v24Frame.java 976 2011-06-08 10:05:34Z paultaylor $

Fields Summary
private static Pattern
validFrameIdentifier
protected static final int
FRAME_DATA_LENGTH_SIZE
protected static final int
FRAME_ID_SIZE
protected static final int
FRAME_FLAGS_SIZE
protected static final int
FRAME_SIZE_SIZE
protected static final int
FRAME_ENCRYPTION_INDICATOR_SIZE
protected static final int
FRAME_GROUPING_INDICATOR_SIZE
protected static final int
FRAME_HEADER_SIZE
private int
encryptionMethod
If the frame is encrypted then the encryption method is stored in this byte
private int
groupIdentifier
If the frame belongs in a group with other frames then the group identifier byte is stored
Constructors Summary
public ID3v24Frame(Lyrics3v2Field field)
Creates a new ID3v2_4Frame datatype based on Lyrics3.

param
field
throws
InvalidTagException

        String id = field.getIdentifier();
        String value;
        if (id.equals("IND"))
        {
            throw new InvalidTagException("Cannot create ID3v2.40 frame from Lyrics3 indications field.");
        }
        else if (id.equals("LYR"))
        {
            FieldFrameBodyLYR lyric = (FieldFrameBodyLYR) field.getBody();
            Lyrics3Line line;
            Iterator<Lyrics3Line> iterator = lyric.iterator();
            FrameBodySYLT sync;
            FrameBodyUSLT unsync;
            boolean hasTimeStamp = lyric.hasTimeStamp();
            // we'll create only one frame here.
            // if there is any timestamp at all, we will create a sync'ed frame.
            sync = new FrameBodySYLT((byte) 0, "ENG", (byte) 2, (byte) 1, "", new byte[0]);
            unsync = new FrameBodyUSLT((byte) 0, "ENG", "", "");
            while (iterator.hasNext())
            {
                line = iterator.next();
                if (hasTimeStamp)
                {
                    // sync.addLyric(line);
                }
                else
                {
                    unsync.addLyric(line);
                }
            }
            if (hasTimeStamp)
            {
                this.frameBody = sync;
                this.frameBody.setHeader(this);
            }
            else
            {
                this.frameBody = unsync;
                this.frameBody.setHeader(this);
            }
        }
        else if (id.equals("INF"))
        {
            value = ((FieldFrameBodyINF) field.getBody()).getAdditionalInformation();
            this.frameBody = new FrameBodyCOMM((byte) 0, "ENG", "", value);
            this.frameBody.setHeader(this);
        }
        else if (id.equals("AUT"))
        {
            value = ((FieldFrameBodyAUT) field.getBody()).getAuthor();
            this.frameBody = new FrameBodyTCOM((byte) 0, value);
            this.frameBody.setHeader(this);
        }
        else if (id.equals("EAL"))
        {
            value = ((FieldFrameBodyEAL) field.getBody()).getAlbum();
            this.frameBody = new FrameBodyTALB((byte) 0, value);
            this.frameBody.setHeader(this);
        }
        else if (id.equals("EAR"))
        {
            value = ((FieldFrameBodyEAR) field.getBody()).getArtist();
            this.frameBody = new FrameBodyTPE1((byte) 0, value);
            this.frameBody.setHeader(this);
        }
        else if (id.equals("ETT"))
        {
            value = ((FieldFrameBodyETT) field.getBody()).getTitle();
            this.frameBody = new FrameBodyTIT2((byte) 0, value);
            this.frameBody.setHeader(this);
        }
        else if (id.equals("IMG"))
        {
            throw new InvalidTagException("Cannot create ID3v2.40 frame from Lyrics3 image field.");
        }
        else
        {
            throw new InvalidTagException("Cannot caret ID3v2.40 frame from " + id + " Lyrics3 field");
        }
    
public ID3v24Frame(ByteBuffer byteBuffer, String loggingFilename)
Creates a new ID3v24Frame datatype by reading from byteBuffer.

param
byteBuffer to read from
param
loggingFilename
throws
org.jaudiotagger.tag.InvalidFrameException

        setLoggingFilename(loggingFilename);
        read(byteBuffer);
    
public ID3v24Frame(ByteBuffer byteBuffer)
Creates a new ID3v24Frame datatype by reading from byteBuffer.

param
byteBuffer to read from
throws
org.jaudiotagger.tag.InvalidFrameException
deprecated
use {@link #ID3v24Frame(ByteBuffer,String)} instead

        this(byteBuffer, "");
    
public ID3v24Frame()

    
public ID3v24Frame(String identifier)
Creates a new ID3v2_4Frame of type identifier. An empty body of the correct type will be automatically created. This constructor should be used when wish to create a new frame from scratch using user input

param
identifier defines the type of body to be created

        //Super Constructor creates a frame with empty body of type specified
        super(identifier);
        statusFlags = new StatusFlags();
        encodingFlags = new EncodingFlags();

    
public ID3v24Frame(ID3v24Frame frame)
Copy Constructor:Creates a new ID3v2_4Frame datatype based on another frame.

param
frame

        super(frame);
        statusFlags = new StatusFlags(frame.getStatusFlags().getOriginalFlags());
        encodingFlags = new EncodingFlags(frame.getEncodingFlags().getFlags());
    
public ID3v24Frame(AbstractID3v2Frame frame)
Creates a new ID3v2_4Frame datatype based on another frame of different version Converts the framebody to the equivalent v24 framebody or to UnsupportedFrameBody if identifier is unknown.

param
frame to construct a new frame from
throws
org.jaudiotagger.tag.InvalidFrameException

        //Should not be called
        if ((frame instanceof ID3v24Frame))
        {
            throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
        }
        //Flags
        if (frame instanceof ID3v23Frame)
        {
            statusFlags = new StatusFlags((ID3v23Frame.StatusFlags) frame.getStatusFlags());
            encodingFlags = new EncodingFlags(frame.getEncodingFlags().getFlags());
        }
        else
        {
            statusFlags = new StatusFlags();
            encodingFlags = new EncodingFlags();
        }

        // Convert Identifier. If the id was a known id for the original
        // version we should be able to convert it to an v24 frame, although it may mean minor
        // modification to the data. If it was not recognised originally it should remain
        // unknown.
        if (frame instanceof ID3v23Frame)
        {
            createV24FrameFromV23Frame((ID3v23Frame) frame);
        }
        else if (frame instanceof ID3v22Frame)
        {
            ID3v23Frame v23Frame = new ID3v23Frame(frame);
            createV24FrameFromV23Frame(v23Frame);
        }
        this.frameBody.setHeader(this);
    
Methods Summary
private voidcheckIfFrameSizeThatIsNotSyncSafe(java.nio.ByteBuffer byteBuffer)
If frame is greater than certain size it will be decoded differently if unsynchronized to if synchronized Frames with certain byte sequences should be unsynchronized but sometimes editors do not unsynchronize them so this method checks both cases and goes with the option that fits best with the data

param
byteBuffer
throws
InvalidFrameException

        if (frameSize > ID3SyncSafeInteger.MAX_SAFE_SIZE)
        {
            //Set Just after size field this is where we want to be when we leave this if statement
            int currentPosition = byteBuffer.position();

            //Read as nonsync safe integer
            byteBuffer.position(currentPosition - getFrameIdSize());
            int nonSyncSafeFrameSize = byteBuffer.getInt();

            //Is the frame size syncsafe, should always be BUT some encoders such as Itunes do not do it properly
            //so do an easy check now.
            byteBuffer.position(currentPosition - getFrameIdSize());
            boolean isNotSyncSafe = ID3SyncSafeInteger.isBufferNotSyncSafe(byteBuffer);

            //not relative so need to move position
            byteBuffer.position(currentPosition);

            if (isNotSyncSafe)
            {
                logger.warning(getLoggingFilename() + ":" + "Frame size is NOT stored as a sync safe integer:" + identifier);

                //This will return a larger frame size so need to check against buffer size if too large then we are
                //buggered , give up
                if (nonSyncSafeFrameSize > (byteBuffer.remaining() - -getFrameFlagsSize()))
                {
                    logger.warning(getLoggingFilename() + ":" + "Invalid Frame size larger than size before mp3 audio:" + identifier);
                    throw new InvalidFrameException(identifier + " is invalid frame");
                }
                else
                {
                    frameSize = nonSyncSafeFrameSize;
                }
            }
            else
            {
                //appears to be sync safe but lets look at the bytes just after the reported end of this
                //frame to see if find a valid frame header

                //Read the Frame Identifier
                byte[] readAheadbuffer = new byte[getFrameIdSize()];
                byteBuffer.position(currentPosition + frameSize + getFrameFlagsSize());

                if (byteBuffer.remaining() < getFrameIdSize())
                {
                    //There is no padding or framedata we are at end so assume syncsafe
                    //reset position to just after framesize
                    byteBuffer.position(currentPosition);
                }
                else
                {
                    byteBuffer.get(readAheadbuffer, 0, getFrameIdSize());

                    //reset position to just after framesize
                    byteBuffer.position(currentPosition);

                    String readAheadIdentifier = new String(readAheadbuffer);
                    if (isValidID3v2FrameIdentifier(readAheadIdentifier))
                    {
                        //Everything ok, so continue
                    }
                    else if (ID3SyncSafeInteger.isBufferEmpty(readAheadbuffer))
                    {
                        //no data found so assume entered padding in which case assume it is last
                        //frame and we are ok
                    }
                    //haven't found identifier so maybe not syncsafe or maybe there are no more frames, just padding
                    else
                    {
                        //Ok lets try using a non-syncsafe integer

                        //size returned will be larger so is it valid
                        if (nonSyncSafeFrameSize > byteBuffer.remaining() - getFrameFlagsSize())
                        {
                            //invalid so assume syncsafe
                            byteBuffer.position(currentPosition);
                        }
                        else
                        {
                            readAheadbuffer = new byte[getFrameIdSize()];
                            byteBuffer.position(currentPosition + nonSyncSafeFrameSize + getFrameFlagsSize());

                            if (byteBuffer.remaining() >= getFrameIdSize())
                            {
                                byteBuffer.get(readAheadbuffer, 0, getFrameIdSize());
                                readAheadIdentifier = new String(readAheadbuffer);

                                //reset position to just after framesize
                                byteBuffer.position(currentPosition);

                                //ok found a valid identifier using non-syncsafe so assume non-syncsafe size
                                //and continue
                                if (isValidID3v2FrameIdentifier(readAheadIdentifier))
                                {
                                    frameSize = nonSyncSafeFrameSize;
                                    logger.warning(getLoggingFilename() + ":" + "Assuming frame size is NOT stored as a sync safe integer:" + identifier);
                                }
                                //no data found so assume entered padding in which case assume it is last
                                //frame and we are ok whereas we didn't hit padding when using syncsafe integer
                                //or we wouldn't have got to this point. So assume syncsafe integer ended within
                                //the frame data whereas this has reached end of frames.
                                else if (ID3SyncSafeInteger.isBufferEmpty(readAheadbuffer))
                                {
                                    frameSize = nonSyncSafeFrameSize;
                                    logger.warning(getLoggingFilename() + ":" + "Assuming frame size is NOT stored as a sync safe integer:" + identifier);
                                }
                                //invalid so assume syncsafe as that is is the standard
                                else
                                {

                                }
                            }
                            else
                            {
                                //reset position to just after framesize
                                byteBuffer.position(currentPosition);

                                //If the unsync framesize matches exactly the remaining bytes then assume it has the
                                //correct size for the last frame
                                if (byteBuffer.remaining() == 0)
                                {
                                    frameSize = nonSyncSafeFrameSize;
                                }
                                //Inconclusive stick with syncsafe
                                else
                                {
                                }
                            }
                        }
                    }
                }
            }
        }
    
public voidcreateStructure()
Return String Representation of body

        MP3File.getStructureFormatter().openHeadingElement(TYPE_FRAME, getIdentifier());
        MP3File.getStructureFormatter().addElement(TYPE_FRAME_SIZE, frameSize);
        statusFlags.createStructure();
        encodingFlags.createStructure();
        frameBody.createStructure();
        MP3File.getStructureFormatter().closeHeadingElement(TYPE_FRAME);
    
private voidcreateV24FrameFromV23Frame(ID3v23Frame frame)

        // Is it a straight conversion e.g TALB - TALB
        identifier = ID3Tags.convertFrameID23To24(frame.getIdentifier());
        logger.finer("Creating V24frame from v23:" + frame.getIdentifier() + ":" + identifier);


        //We cant convert unsupported bodies properly
        if (frame.getBody() instanceof FrameBodyUnsupported)
        {
            this.frameBody = new FrameBodyUnsupported((FrameBodyUnsupported) frame.getBody());
            this.frameBody.setHeader(this);
            identifier = frame.getIdentifier();
            logger.finer("V3:UnsupportedBody:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
        }//Simple Copy
        else if (identifier != null)
        {
            //Special Case
            if ((frame.getIdentifier().equals(ID3v23Frames.FRAME_ID_V3_USER_DEFINED_INFO)) && (((FrameBodyTXXX) frame.getBody()).getDescription().equals(FrameBodyTXXX.MOOD)))
            {
                this.frameBody = new FrameBodyTMOO((FrameBodyTXXX) frame.getBody());
                this.frameBody.setHeader(this);
                identifier = frameBody.getIdentifier();
            }
            else
            {
                logger.finer("V3:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
                this.frameBody = (AbstractTagFrameBody) ID3Tags.copyObject(frame.getBody());
                this.frameBody.setHeader(this);
            }
        }
        // Is it a known v3 frame which needs forcing to v4 frame e.g. TYER - TDRC
        else if (ID3Tags.isID3v23FrameIdentifier(frame.getIdentifier()))
        {
            identifier = ID3Tags.forceFrameID23To24(frame.getIdentifier());
            if (identifier != null)
            {
                logger.config("V3:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
                this.frameBody = this.readBody(identifier, (AbstractID3v2FrameBody) frame.getBody());
                this.frameBody.setHeader(this);
            }
            // No mechanism exists to convert it to a v24 frame, e.g deprecated frame e.g TSIZ, so hold
            // as a deprecated frame consisting of an array of bytes*/
            else
            {
                this.frameBody = new FrameBodyDeprecated((AbstractID3v2FrameBody) frame.getBody());
                this.frameBody.setHeader(this);
                identifier = frame.getIdentifier();
                logger.finer("V3:Deprecated:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
            }
        }
        // Unknown Frame e.g NCON or TDRL (because TDRL unknown to V23)
        else
        {
            this.frameBody = new FrameBodyUnsupported((FrameBodyUnsupported) frame.getBody());
            this.frameBody.setHeader(this);
            identifier = frame.getIdentifier();
            logger.finer("V3:Unknown:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
        }
    
public booleanequals(java.lang.Object obj)

param
obj
return
if obj is equivalent to this frame


        if (this == obj) return true;

        if (!(obj instanceof ID3v24Frame))
        {
            return false;
        }
        ID3v24Frame that = (ID3v24Frame) obj;


        return EqualsUtil.areEqual(this.statusFlags, that.statusFlags) && EqualsUtil.areEqual(this.encodingFlags, that.encodingFlags) && super.equals(that);
    
public AbstractID3v2Frame.EncodingFlagsgetEncodingFlags()
Get Encoding Flags Object

        return encodingFlags;
    
public intgetEncryptionMethod()

        return encryptionMethod;
    
protected intgetFrameFlagsSize()

        return FRAME_FLAGS_SIZE;
    
protected intgetFrameHeaderSize()

        return FRAME_HEADER_SIZE;
    
protected intgetFrameIdSize()


      
    
        return FRAME_ID_SIZE;
    
private voidgetFrameSize(java.nio.ByteBuffer byteBuffer)
Read the frame size form the header, check okay , if not try to fix or just throw exception

param
byteBuffer
throws
InvalidFrameException

         //Read frame size as syncsafe integer
        frameSize = ID3SyncSafeInteger.bufferToValue(byteBuffer);

        if (frameSize < 0)
        {
            logger.warning(getLoggingFilename() + ":" + "Invalid Frame size:" + identifier);
            throw new InvalidFrameException(identifier + " is invalid frame");
        }
        else if (frameSize == 0)
        {
            logger.warning(getLoggingFilename() + ":" + "Empty Frame:" + identifier);
            //We dont process this frame or add to framemap becuase contains no useful information
            //Skip the two flag bytes so in correct position for subsequent frames
            byteBuffer.get();
            byteBuffer.get();
            throw new EmptyFrameException(identifier + " is empty frame");
        }
        else if (frameSize > (byteBuffer.remaining() - FRAME_FLAGS_SIZE))
        {
            logger.warning(getLoggingFilename() + ":" + "Invalid Frame size larger than size before mp3 audio:" + identifier);
            throw new InvalidFrameException(identifier + " is invalid frame");
        }

        checkIfFrameSizeThatIsNotSyncSafe(byteBuffer);
    
protected intgetFrameSizeSize()

        return FRAME_SIZE_SIZE;
    
public intgetGroupIdentifier()

        return groupIdentifier;
    
public intgetSize()
Return size of frame

return
int frame size

        return frameBody.getSize() + ID3v24Frame.FRAME_HEADER_SIZE;
    
public AbstractID3v2Frame.StatusFlagsgetStatusFlags()
Get Status Flags Object

        return statusFlags;
    
public booleanisBinary()

return
true if considered a common frame

        return ID3v24Frames.getInstanceOf().isBinary(getId());
    
public booleanisCommon()

return
true if considered a common frame

        return ID3v24Frames.getInstanceOf().isCommon(getId());
    
public booleanisValidID3v2FrameIdentifier(java.lang.String identifier)
Does the frame identifier meet the syntax for a idv3v2 frame identifier. must start with a capital letter and only contain capital letters and numbers

param
identifier to be checked
return
whether the identifier is valid

        Matcher m = ID3v24Frame.validFrameIdentifier.matcher(identifier);
        return m.matches();
    
public voidread(java.nio.ByteBuffer byteBuffer)
Read the frame from the specified file. Read the frame header then delegate reading of data to frame body.

param
byteBuffer to read the frame from

        String identifier = readIdentifier(byteBuffer);

        //Is this a valid identifier?
        if (!isValidID3v2FrameIdentifier(identifier))
        {
            //If not valid move file pointer back to one byte after
            //the original check so can try again.
            logger.config(getLoggingFilename() + ":" + "Invalid identifier:" + identifier);
            byteBuffer.position(byteBuffer.position() - (getFrameIdSize() - 1));
            throw new InvalidFrameIdentifierException(getLoggingFilename() + ":" + identifier + ":is not a valid ID3v2.30 frame");
        }

        //Get the frame size, adjusted as necessary
        getFrameSize(byteBuffer);

        //Read the flag bytes
        statusFlags = new StatusFlags(byteBuffer.get());
        encodingFlags = new EncodingFlags(byteBuffer.get());

        //Read extra bits appended to frame header for various encodings
        //These are not included in header size but are included in frame size but wont be read when we actually
        //try to read the frame body data
        int extraHeaderBytesCount = 0;
        int dataLengthSize = -1;
        if (((EncodingFlags) encodingFlags).isGrouping())
        {
            extraHeaderBytesCount = ID3v24Frame.FRAME_GROUPING_INDICATOR_SIZE;
            groupIdentifier=byteBuffer.get();
        }

        if (((EncodingFlags) encodingFlags).isEncryption())
        {
            //Read the Encryption byte, but do nothing with it
            extraHeaderBytesCount += ID3v24Frame.FRAME_ENCRYPTION_INDICATOR_SIZE;
            encryptionMethod=byteBuffer.get();
        }

        if (((EncodingFlags) encodingFlags).isDataLengthIndicator())
        {
            //Read the sync safe size field
            dataLengthSize = ID3SyncSafeInteger.bufferToValue(byteBuffer);
            extraHeaderBytesCount += FRAME_DATA_LENGTH_SIZE;
            logger.config(getLoggingFilename() + ":" + "Frame Size Is:" + frameSize + " Data Length Size:" + dataLengthSize);
        }

        //Work out the real size of the frameBody data
        int realFrameSize = frameSize - extraHeaderBytesCount;

        //Create Buffer that only contains the body of this frame rather than the remainder of tag
        ByteBuffer frameBodyBuffer = byteBuffer.slice();
        frameBodyBuffer.limit(realFrameSize);

        //Do we need to synchronize the frame body
        int syncSize = realFrameSize;
        if (((EncodingFlags) encodingFlags).isUnsynchronised())
        {
            //We only want to synchronize the buffer up to the end of this frame (remember this
            //buffer contains the remainder of this tag not just this frame), and we cannot just
            //create a new buffer because when this method returns the position of the buffer is used
            //to look for the next frame, so we need to modify the buffer. The action of synchronizing causes
            //bytes to be dropped so the existing buffer is large enough to hold the modifications
            frameBodyBuffer = ID3Unsynchronization.synchronize(frameBodyBuffer);
            syncSize = frameBodyBuffer.limit();
            logger.config(getLoggingFilename() + ":" + "Frame Size After Syncing is:" + syncSize);
        }

        //Read the body data
        try
        {
            if (((EncodingFlags) encodingFlags).isCompression())
            {
                frameBodyBuffer = ID3Compression.uncompress(identifier, getLoggingFilename(), byteBuffer, dataLengthSize, realFrameSize);
                frameBody = readBody(identifier, frameBodyBuffer, dataLengthSize);
            }
            else if (((EncodingFlags) encodingFlags).isEncryption())
            {
                frameBodyBuffer = byteBuffer.slice();
                frameBodyBuffer.limit(realFrameSize);
                frameBody = readEncryptedBody(identifier, byteBuffer,frameSize);
            }
            else
            {
                frameBody = readBody(identifier, frameBodyBuffer, syncSize);
            }
            if (!(frameBody instanceof ID3v24FrameBody))
            {
                logger.config(getLoggingFilename() + ":" + "Converted frame body with:" + identifier + " to deprecated framebody");
                frameBody = new FrameBodyDeprecated((AbstractID3v2FrameBody) frameBody);
            }
        }
        finally
        {
            //Update position of main buffer, so no attempt is made to reread these bytes
            byteBuffer.position(byteBuffer.position() + realFrameSize);
        }
    
public voidsetEncoding(java.lang.String encoding)
Sets the charset encoding used by the field.

param
encoding charset.

        Integer encodingId = TextEncoding.getInstanceOf().getIdForValue(encoding);
        if(encoding!=null)
        {
            if(encodingId <4)
            {
                this.getBody().setTextEncoding(encodingId.byteValue());
            }
        }
    
public voidwrite(java.io.ByteArrayOutputStream tagBuffer)
Write the frame. Writes the frame header but writing the data is delegated to the frame body.

throws
IOException

        boolean unsynchronization;

        logger.config("Writing frame to file:" + getIdentifier());

        //This is where we will write header, move position to where we can
        //write bodybuffer
        ByteBuffer headerBuffer = ByteBuffer.allocate(FRAME_HEADER_SIZE);

        //Write Frame Body Data to a new stream
        ByteArrayOutputStream bodyOutputStream = new ByteArrayOutputStream();
        ((AbstractID3v2FrameBody) frameBody).write(bodyOutputStream);

        //Does it need unsynchronizing, and are we allowing unsychronizing
        byte[] bodyBuffer = bodyOutputStream.toByteArray();
        unsynchronization = TagOptionSingleton.getInstance().isUnsyncTags() && ID3Unsynchronization.requiresUnsynchronization(bodyBuffer);
        if (unsynchronization)
        {
            bodyBuffer = ID3Unsynchronization.unsynchronize(bodyBuffer);
            logger.config("bodybytebuffer:sizeafterunsynchronisation:" + bodyBuffer.length);
        }

        //Write Frame Header
        //Write Frame ID, the identifier must be 4 bytes bytes long it may not be
        //because converted an unknown v2.2 id (only 3 bytes long)
        if (getIdentifier().length() == 3)
        {
            identifier = identifier + ' ";
        }
        headerBuffer.put(Utils.getDefaultBytes(getIdentifier(), "ISO-8859-1"), 0, FRAME_ID_SIZE);

        //Write Frame Size based on size of body buffer (if it has been unsynced then it size
        //will have increased accordingly
        int size = bodyBuffer.length;
        logger.fine("Frame Size Is:" + size);
        headerBuffer.put(ID3SyncSafeInteger.valueToBuffer(size));

        //Write the Flags
        //Status Flags:leave as they were when we read
        headerBuffer.put(statusFlags.getWriteFlags());

        //Remove any non standard flags
        ((ID3v24Frame.EncodingFlags) encodingFlags).unsetNonStandardFlags();
                
        //Encoding we only support unsynchronization
        if (unsynchronization)
        {
            ((ID3v24Frame.EncodingFlags) encodingFlags).setUnsynchronised();
        }
        else
        {
            ((ID3v24Frame.EncodingFlags) encodingFlags).unsetUnsynchronised();
        }
        //These are not currently supported on write
        ((ID3v24Frame.EncodingFlags) encodingFlags).unsetCompression();
        ((ID3v24Frame.EncodingFlags) encodingFlags).unsetDataLengthIndicator();
        headerBuffer.put(encodingFlags.getFlags());

        try
        {
            //Add header to the Byte Array Output Stream
            tagBuffer.write(headerBuffer.array());

            if (((EncodingFlags) encodingFlags).isEncryption())
            {
               tagBuffer.write(encryptionMethod);
            }

            if (((EncodingFlags) encodingFlags).isGrouping())
            {
                tagBuffer.write(groupIdentifier);
            }

            //Add bodybuffer to the Byte Array Output Stream
            tagBuffer.write(bodyBuffer);
        }
        catch (IOException ioe)
        {
            //This could never happen coz not writing to file, so convert to RuntimeException
            throw new RuntimeException(ioe);
        }