2
* MusicTag Copyright (C)2003,2004
4
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
5
* General Public License as published by the Free Software Foundation; either version 2.1 of the License,
6
* or (at your option) any later version.
8
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
9
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
* See the GNU Lesser General Public License for more details.
12
* You should have received a copy of the GNU Lesser General Public License along with this library; if not,
13
* you can get a copy from http://www.opensource.org/licenses/lgpl-license.php or write to the Free Software
14
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
package org.jaudiotagger.tag.id3;
18
import org.jaudiotagger.audio.generic.Utils;
19
import org.jaudiotagger.audio.mp3.MP3File;
20
import org.jaudiotagger.tag.EmptyFrameException;
21
import org.jaudiotagger.tag.InvalidFrameException;
22
import org.jaudiotagger.tag.InvalidFrameIdentifierException;
23
import org.jaudiotagger.tag.id3.framebody.AbstractID3v2FrameBody;
24
import org.jaudiotagger.tag.id3.framebody.FrameBodyDeprecated;
25
import org.jaudiotagger.tag.id3.framebody.FrameBodyUnsupported;
27
import java.io.ByteArrayOutputStream;
28
import java.io.IOException;
29
import java.math.BigInteger;
30
import java.nio.ByteBuffer;
31
import java.util.logging.Level;
32
import java.util.regex.Matcher;
33
import java.util.regex.Pattern;
36
* Represents an ID3v2.2 frame.
38
* @author : Paul Taylor
39
* @author : Eric Farng
40
* @version $Id: ID3v22Frame.java,v 1.34 2009/11/12 15:42:57 paultaylor Exp $
42
public class ID3v22Frame extends AbstractID3v2Frame
44
private static Pattern validFrameIdentifier = Pattern.compile("[A-Z][0-9A-Z]{2}");
46
protected static final int FRAME_ID_SIZE = 3;
47
protected static final int FRAME_SIZE_SIZE = 3;
48
protected static final int FRAME_HEADER_SIZE = FRAME_ID_SIZE + FRAME_SIZE_SIZE;
56
* Creates a new ID3v22 Frame with given body
58
* @param body New body and frame is based on this
60
public ID3v22Frame(AbstractID3v2FrameBody body)
66
* Creates a new ID3v22 Frame of type identifier.
68
* An empty body of the correct type will be automatically created. This constructor should be used when wish to
69
* create a new frame from scratch using user values
72
@SuppressWarnings("unchecked")
73
public ID3v22Frame(String identifier)
76
logger.info("Creating empty frame of type" + identifier);
77
String bodyIdentifier = identifier;
78
this.identifier = identifier;
80
//If dealing with v22 identifier (Note this constructor is used by all three tag versions)
81
if (ID3Tags.isID3v22FrameIdentifier(bodyIdentifier))
83
//Does it have its own framebody (PIC,CRM) or are we using v23/v24 body (the normal case)
84
if (ID3Tags.forceFrameID22To23(bodyIdentifier) != null)
88
//TODO Improve messy fix for datetime
89
//TODO need to check in case v22 body does exist before using V23 body(e.g PIC)
91
if ((bodyIdentifier.equals(ID3v22Frames.FRAME_ID_V2_TYER)) || (bodyIdentifier.equals(ID3v22Frames.FRAME_ID_V2_TIME)))
93
bodyIdentifier = ID3v24Frames.FRAME_ID_YEAR;
95
// Have to check for v22 because most don't have own body they use v23 or v24
96
// body to hold the data, the frame is identified by its identifier, the body identifier
97
// is just to create a body suitable for writing the data to
98
else if (ID3Tags.isID3v22FrameIdentifier(bodyIdentifier))
100
bodyIdentifier = ID3Tags.convertFrameID22To23(bodyIdentifier);
104
// Use reflection to map id to frame body, which makes things much easier
105
// to keep things up to date.
108
Class<AbstractID3v2FrameBody> c = (Class<AbstractID3v2FrameBody>) Class.forName("org.jaudiotagger.tag.id3.framebody.FrameBody" + bodyIdentifier);
109
frameBody = c.newInstance();
111
catch (ClassNotFoundException cnfe)
113
logger.log(Level.SEVERE, cnfe.getMessage(), cnfe);
114
frameBody = new FrameBodyUnsupported(identifier);
116
//Instantiate Interface/Abstract should not happen
117
catch (InstantiationException ie)
119
logger.log(Level.SEVERE, ie.getMessage(), ie);
120
throw new RuntimeException(ie);
122
//Private Constructor shouild not happen
123
catch (IllegalAccessException iae)
125
logger.log(Level.SEVERE, iae.getMessage(), iae);
126
throw new RuntimeException(iae);
128
frameBody.setHeader(this);
129
logger.info("Created empty frame of type" + this.identifier + "with frame body of" + bodyIdentifier);
136
* Creates a new v22 frame based on another v22 frame
139
public ID3v22Frame(ID3v22Frame frame)
142
logger.info("Creating frame from a frame of same version");
145
private void createV22FrameFromV23Frame(ID3v23Frame frame) throws InvalidFrameException
147
identifier = ID3Tags.convertFrameID23To22(frame.getIdentifier());
148
if (identifier != null)
150
logger.info("V2:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
151
this.frameBody = (AbstractID3v2FrameBody) ID3Tags.copyObject(frame.getBody());
153
// Is it a known v3 frame which needs forcing to v2 frame e.g. APIC - PIC
154
else if (ID3Tags.isID3v23FrameIdentifier(frame.getIdentifier()))
156
identifier = ID3Tags.forceFrameID23To22(frame.getIdentifier());
157
if (identifier != null)
159
logger.info("V2:Force:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
160
this.frameBody = this.readBody(identifier, (AbstractID3v2FrameBody) frame.getBody());
162
// No mechanism exists to convert it to a v22 frame
165
throw new InvalidFrameException("Unable to convert v23 frame:" + frame.getIdentifier() + " to a v22 frame");
168
//Deprecated frame for v23
169
else if (frame.getBody() instanceof FrameBodyDeprecated)
171
//Was it valid for this tag version, if so try and reconstruct
172
if (ID3Tags.isID3v22FrameIdentifier(frame.getIdentifier()))
174
this.frameBody = frame.getBody();
175
identifier = frame.getIdentifier();
176
logger.info("DEPRECATED:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
178
//or was it still deprecated, if so leave as is
181
this.frameBody = new FrameBodyDeprecated((FrameBodyDeprecated) frame.getBody());
182
identifier = frame.getIdentifier();
183
logger.info("DEPRECATED:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
186
// Unknown Frame e.g NCON
189
this.frameBody = new FrameBodyUnsupported((FrameBodyUnsupported) frame.getBody());
190
identifier = frame.getIdentifier();
191
logger.info("v2:UNKNOWN:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
196
* Creates a new ID3v22 Frame from another frame of a different tag version
198
* @param frame to construct the new frame from
199
* @throws org.jaudiotagger.tag.InvalidFrameException
201
public ID3v22Frame(AbstractID3v2Frame frame) throws InvalidFrameException
203
logger.info("Creating frame from a frame of a different version");
204
if (frame instanceof ID3v22Frame)
206
throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
209
// If it is a v24 frame is it possible to convert it into a v23 frame, anmd then convert from that
210
if (frame instanceof ID3v24Frame)
212
ID3v23Frame v23Frame = new ID3v23Frame(frame);
213
createV22FrameFromV23Frame(v23Frame);
215
//If it is a v23 frame is it possible to convert it into a v22 frame
216
else if (frame instanceof ID3v23Frame)
218
createV22FrameFromV23Frame((ID3v23Frame) frame);
220
this.frameBody.setHeader(this);
221
logger.info("Created frame from a frame of a different version");
225
* Creates a new ID3v22Frame datatype by reading from byteBuffer.
227
* @param byteBuffer to read from
228
* @param loggingFilename
229
* @throws org.jaudiotagger.tag.InvalidFrameException
231
public ID3v22Frame(ByteBuffer byteBuffer, String loggingFilename) throws InvalidFrameException
233
setLoggingFilename(loggingFilename);
238
* Creates a new ID3v23Frame datatype by reading from byteBuffer.
240
* @param byteBuffer to read from
241
* @deprecated use {@link #ID3v22Frame(ByteBuffer,String)} instead
242
* @throws org.jaudiotagger.tag.InvalidFrameException
244
public ID3v22Frame(ByteBuffer byteBuffer) throws InvalidFrameException
246
this(byteBuffer, "");
250
* Return size of frame
252
* @return int size of frame
256
return frameBody.getSize() + FRAME_HEADER_SIZE;
260
* Read frame from file.
261
* Read the frame header then delegate reading of data to frame body.
265
public void read(ByteBuffer byteBuffer) throws InvalidFrameException
267
byte[] buffer = new byte[FRAME_ID_SIZE];
269
if (byteBuffer.position() + FRAME_HEADER_SIZE >= byteBuffer.limit())
271
logger.warning("No space to find another frame:");
272
throw new InvalidFrameException(" No space to find another frame");
275
// Read the FrameID Identifier
276
byteBuffer.get(buffer, 0, FRAME_ID_SIZE);
277
identifier = new String(buffer);
278
logger.info("Read Frame from file identifier is:" + identifier);
280
// Is this a valid identifier?
281
if (!isValidID3v2FrameIdentifier(identifier))
283
logger.info("Invalid identifier:" + identifier);
284
byteBuffer.position(byteBuffer.position() - (FRAME_ID_SIZE - 1));
285
throw new InvalidFrameIdentifierException(identifier + " is not a valid ID3v2.20 frame");
287
//Read Frame Size (same size as Frame Id so reuse buffer)
288
byteBuffer.get(buffer, 0, FRAME_SIZE_SIZE);
289
frameSize = decodeSize(buffer);
292
throw new InvalidFrameException(identifier + " has invalid size of:" + frameSize);
294
else if (frameSize == 0)
296
//We dont process this frame or add to framemap becuase contains no useful information
297
logger.warning("Empty Frame:" + identifier);
298
throw new EmptyFrameException(identifier + " is empty frame");
300
else if (frameSize > byteBuffer.remaining())
302
logger.warning("Invalid Frame size larger than size before mp3 audio:" + identifier);
303
throw new InvalidFrameException(identifier + " is invalid frame");
307
logger.fine("Frame Size Is:" + frameSize);
308
//Convert v2.2 to v2.4 id just for reading the data
309
String id = ID3Tags.convertFrameID22To24(identifier);
312
//OK,it may be convertable to a v.3 id even though not valid v.4
313
id = ID3Tags.convertFrameID22To23(identifier);
316
// Is it a valid v22 identifier so should be able to find a
317
// frame body for it.
318
if (ID3Tags.isID3v22FrameIdentifier(identifier))
322
// Unknown so will be created as FrameBodyUnsupported
329
logger.fine("Identifier was:" + identifier + " reading using:" + id);
331
//Create Buffer that only contains the body of this frame rather than the remainder of tag
332
ByteBuffer frameBodyBuffer = byteBuffer.slice();
333
frameBodyBuffer.limit(frameSize);
337
frameBody = readBody(id, frameBodyBuffer, frameSize);
341
//Update position of main buffer, so no attempt is made to reread these bytes
342
byteBuffer.position(byteBuffer.position() + frameSize);
348
* Read Frame Size, which has to be decoded
352
private int decodeSize(byte[] buffer)
354
BigInteger bi = new BigInteger(buffer);
355
int tmpSize = bi.intValue();
358
logger.warning("Invalid Frame Size of:" + tmpSize + "Decoded from bin:" + Integer.toBinaryString(tmpSize) + "Decoded from hex:" + Integer.toHexString(tmpSize));
365
* Write Frame raw data
367
* @throws IOException
369
public void write(ByteArrayOutputStream tagBuffer)
371
logger.info("Write Frame to Buffer" + getIdentifier());
372
//This is where we will write header, move position to where we can
374
ByteBuffer headerBuffer = ByteBuffer.allocate(FRAME_HEADER_SIZE);
376
//Write Frame Body Data
377
ByteArrayOutputStream bodyOutputStream = new ByteArrayOutputStream();
378
((AbstractID3v2FrameBody) frameBody).write(bodyOutputStream);
381
//Write Frame ID must adjust can only be 3 bytes long
382
headerBuffer.put(Utils.getDefaultBytes(getIdentifier(), "ISO-8859-1"), 0, FRAME_ID_SIZE);
383
encodeSize(headerBuffer, frameBody.getSize());
385
//Add header to the Byte Array Output Stream
388
tagBuffer.write(headerBuffer.array());
390
//Add body to the Byte Array Output Stream
391
tagBuffer.write(bodyOutputStream.toByteArray());
393
catch (IOException ioe)
395
//This could never happen coz not writing to file, so convert to RuntimeException
396
throw new RuntimeException(ioe);
401
* Write Frame Size (can now be accurately calculated, have to convert 4 byte int
403
* @param headerBuffer
406
private void encodeSize(ByteBuffer headerBuffer, int size)
408
headerBuffer.put((byte) ((size & 0x00FF0000) >> 16));
409
headerBuffer.put((byte) ((size & 0x0000FF00) >> 8));
410
headerBuffer.put((byte) (size & 0x000000FF));
411
logger.fine("Frame Size Is Actual:" + size + ":Encoded bin:" + Integer.toBinaryString(size) + ":Encoded Hex" + Integer.toHexString(size));
415
* Does the frame identifier meet the syntax for a idv3v2 frame identifier.
416
* must start with a capital letter and only contain capital letters and numbers
421
public boolean isValidID3v2FrameIdentifier(String identifier)
423
Matcher m = ID3v22Frame.validFrameIdentifier.matcher(identifier);
428
* Return String Representation of body
430
public void createStructure()
432
MP3File.getStructureFormatter().openHeadingElement(TYPE_FRAME, getIdentifier());
433
MP3File.getStructureFormatter().addElement(TYPE_FRAME_SIZE, frameSize);
434
frameBody.createStructure();
435
MP3File.getStructureFormatter().closeHeadingElement(TYPE_FRAME);
439
* @return true if considered a common frame
441
public boolean isCommon()
443
return ID3v22Frames.getInstanceOf().isCommon(getId());
447
* @return true if considered a common frame
449
public boolean isBinary()
451
return ID3v22Frames.getInstanceOf().isBinary(getId());
2
* MusicTag Copyright (C)2003,2004
4
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
5
* General Public License as published by the Free Software Foundation; either version 2.1 of the License,
6
* or (at your option) any later version.
8
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
9
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
* See the GNU Lesser General Public License for more details.
12
* You should have received a copy of the GNU Lesser General Public License along with this library; if not,
13
* you can get a copy from http://www.opensource.org/licenses/lgpl-license.php or write to the Free Software
14
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
package org.jaudiotagger.tag.id3;
18
import org.jaudiotagger.audio.generic.Utils;
19
import org.jaudiotagger.audio.mp3.MP3File;
20
import org.jaudiotagger.tag.*;
21
import org.jaudiotagger.tag.id3.framebody.AbstractID3v2FrameBody;
22
import org.jaudiotagger.tag.id3.framebody.FrameBodyDeprecated;
23
import org.jaudiotagger.tag.id3.framebody.FrameBodyUnsupported;
24
import org.jaudiotagger.tag.id3.valuepair.TextEncoding;
25
import org.jaudiotagger.utils.EqualsUtil;
27
import java.io.ByteArrayOutputStream;
28
import java.io.IOException;
29
import java.math.BigInteger;
30
import java.nio.ByteBuffer;
31
import java.util.logging.Level;
32
import java.util.regex.Matcher;
33
import java.util.regex.Pattern;
36
* Represents an ID3v2.2 frame.
38
* @author : Paul Taylor
39
* @author : Eric Farng
40
* @version $Id: ID3v22Frame.java 929 2010-11-17 12:36:46Z paultaylor $
42
public class ID3v22Frame extends AbstractID3v2Frame
44
private static Pattern validFrameIdentifier = Pattern.compile("[A-Z][0-9A-Z]{2}");
46
protected static final int FRAME_ID_SIZE = 3;
47
protected static final int FRAME_SIZE_SIZE = 3;
48
protected static final int FRAME_HEADER_SIZE = FRAME_ID_SIZE + FRAME_SIZE_SIZE;
55
protected int getFrameIdSize()
60
protected int getFrameSizeSize()
62
return FRAME_SIZE_SIZE;
65
protected int getFrameHeaderSize()
67
return FRAME_HEADER_SIZE;
71
* Creates a new ID3v22 Frame with given body
73
* @param body New body and frame is based on this
75
public ID3v22Frame(AbstractID3v2FrameBody body)
81
* Compare for equality
82
* To be deemed equal obj must be a IDv23Frame with the same identifier
84
* containing the same body,datatype list ectera.
85
* equals() method is made up from all the various components
88
* @return if true if this object is equivalent to obj
90
public boolean equals(Object obj)
92
if ( this == obj ) return true;
94
if (!(obj instanceof ID3v22Frame))
98
ID3v22Frame that = (ID3v22Frame) obj;
102
EqualsUtil.areEqual(this.statusFlags, that.statusFlags) &&
103
EqualsUtil.areEqual(this.encodingFlags, that.encodingFlags) &&
109
* Creates a new ID3v22 Frame of type identifier.
111
* An empty body of the correct type will be automatically created. This constructor should be used when wish to
112
* create a new frame from scratch using user values
115
@SuppressWarnings("unchecked")
116
public ID3v22Frame(String identifier)
119
logger.info("Creating empty frame of type" + identifier);
120
String bodyIdentifier = identifier;
121
this.identifier = identifier;
123
//If dealing with v22 identifier (Note this constructor is used by all three tag versions)
124
if (ID3Tags.isID3v22FrameIdentifier(bodyIdentifier))
126
//Does it have its own framebody (PIC,CRM) or are we using v23/v24 body (the normal case)
127
if (ID3Tags.forceFrameID22To23(bodyIdentifier) != null)
131
else if(bodyIdentifier.equals("CRM"))
134
//TODO we don't have a way of converting this to v23 which is why its not in the ForceMap
136
//TODO Improve messy fix for datetime
137
//TODO need to check in case v22 body does exist before using V23 body(e.g PIC)
138
else if ((bodyIdentifier.equals(ID3v22Frames.FRAME_ID_V2_TYER)) || (bodyIdentifier.equals(ID3v22Frames.FRAME_ID_V2_TIME)))
140
bodyIdentifier = ID3v24Frames.FRAME_ID_YEAR;
142
// Have to check for v22 because most don't have own body they use v23 or v24
143
// body to hold the data, the frame is identified by its identifier, the body identifier
144
// is just to create a body suitable for writing the data to
145
else if (ID3Tags.isID3v22FrameIdentifier(bodyIdentifier))
147
bodyIdentifier = ID3Tags.convertFrameID22To23(bodyIdentifier);
151
// Use reflection to map id to frame body, which makes things much easier
152
// to keep things up to date.
155
Class<AbstractID3v2FrameBody> c = (Class<AbstractID3v2FrameBody>) Class.forName("org.jaudiotagger.tag.id3.framebody.FrameBody" + bodyIdentifier);
156
frameBody = c.newInstance();
158
catch (ClassNotFoundException cnfe)
160
logger.log(Level.SEVERE, cnfe.getMessage(), cnfe);
161
frameBody = new FrameBodyUnsupported(identifier);
163
//Instantiate Interface/Abstract should not happen
164
catch (InstantiationException ie)
166
logger.log(Level.SEVERE, ie.getMessage(), ie);
167
throw new RuntimeException(ie);
169
//Private Constructor shouild not happen
170
catch (IllegalAccessException iae)
172
logger.log(Level.SEVERE, iae.getMessage(), iae);
173
throw new RuntimeException(iae);
175
frameBody.setHeader(this);
176
logger.info("Created empty frame of type" + this.identifier + "with frame body of" + bodyIdentifier);
183
* Creates a new v22 frame based on another v22 frame
186
public ID3v22Frame(ID3v22Frame frame)
189
logger.info("Creating frame from a frame of same version");
192
private void createV22FrameFromV23Frame(ID3v23Frame frame) throws InvalidFrameException
194
identifier = ID3Tags.convertFrameID23To22(frame.getIdentifier());
195
if (identifier != null)
197
logger.info("V2:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
198
this.frameBody = (AbstractID3v2FrameBody) ID3Tags.copyObject(frame.getBody());
200
// Is it a known v3 frame which needs forcing to v2 frame e.g. APIC - PIC
201
else if (ID3Tags.isID3v23FrameIdentifier(frame.getIdentifier()))
203
identifier = ID3Tags.forceFrameID23To22(frame.getIdentifier());
204
if (identifier != null)
206
logger.info("V2:Force:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
207
this.frameBody = this.readBody(identifier, (AbstractID3v2FrameBody) frame.getBody());
209
// No mechanism exists to convert it to a v22 frame
212
throw new InvalidFrameException("Unable to convert v23 frame:" + frame.getIdentifier() + " to a v22 frame");
215
//Deprecated frame for v23
216
else if (frame.getBody() instanceof FrameBodyDeprecated)
218
//Was it valid for this tag version, if so try and reconstruct
219
if (ID3Tags.isID3v22FrameIdentifier(frame.getIdentifier()))
221
this.frameBody = frame.getBody();
222
identifier = frame.getIdentifier();
223
logger.info("DEPRECATED:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
225
//or was it still deprecated, if so leave as is
228
this.frameBody = new FrameBodyDeprecated((FrameBodyDeprecated) frame.getBody());
229
identifier = frame.getIdentifier();
230
logger.info("DEPRECATED:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
233
// Unknown Frame e.g NCON
236
this.frameBody = new FrameBodyUnsupported((FrameBodyUnsupported) frame.getBody());
237
identifier = frame.getIdentifier();
238
logger.info("v2:UNKNOWN:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
243
* Creates a new ID3v22 Frame from another frame of a different tag version
245
* @param frame to construct the new frame from
246
* @throws org.jaudiotagger.tag.InvalidFrameException
248
public ID3v22Frame(AbstractID3v2Frame frame) throws InvalidFrameException
250
logger.info("Creating frame from a frame of a different version");
251
if (frame instanceof ID3v22Frame)
253
throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
256
// If it is a v24 frame is it possible to convert it into a v23 frame, anmd then convert from that
257
if (frame instanceof ID3v24Frame)
259
ID3v23Frame v23Frame = new ID3v23Frame(frame);
260
createV22FrameFromV23Frame(v23Frame);
262
//If it is a v23 frame is it possible to convert it into a v22 frame
263
else if (frame instanceof ID3v23Frame)
265
createV22FrameFromV23Frame((ID3v23Frame) frame);
267
this.frameBody.setHeader(this);
268
logger.info("Created frame from a frame of a different version");
272
* Creates a new ID3v22Frame datatype by reading from byteBuffer.
274
* @param byteBuffer to read from
275
* @param loggingFilename
276
* @throws org.jaudiotagger.tag.InvalidFrameException
278
public ID3v22Frame(ByteBuffer byteBuffer, String loggingFilename) throws InvalidFrameException, InvalidDataTypeException
280
setLoggingFilename(loggingFilename);
285
* Creates a new ID3v23Frame datatype by reading from byteBuffer.
287
* @param byteBuffer to read from
288
* @deprecated use {@link #ID3v22Frame(ByteBuffer,String)} instead
289
* @throws org.jaudiotagger.tag.InvalidFrameException
291
public ID3v22Frame(ByteBuffer byteBuffer) throws InvalidFrameException, InvalidDataTypeException
293
this(byteBuffer, "");
297
* Return size of frame
299
* @return int size of frame
303
return frameBody.getSize() + getFrameHeaderSize();
307
protected boolean isPadding(byte[] buffer)
321
* Read frame from file.
322
* Read the frame header then delegate reading of data to frame body.
326
public void read(ByteBuffer byteBuffer) throws InvalidFrameException, InvalidDataTypeException
328
String identifier = readIdentifier(byteBuffer);
330
byte[] buffer = new byte[getFrameSizeSize()];
332
// Is this a valid identifier?
333
if (!isValidID3v2FrameIdentifier(identifier))
335
logger.info("Invalid identifier:" + identifier);
336
byteBuffer.position(byteBuffer.position() - (getFrameIdSize() - 1));
337
throw new InvalidFrameIdentifierException(getLoggingFilename() + ":" + identifier + ":is not a valid ID3v2.20 frame");
339
//Read Frame Size (same size as Frame Id so reuse buffer)
340
byteBuffer.get(buffer, 0, getFrameSizeSize());
341
frameSize = decodeSize(buffer);
344
throw new InvalidFrameException(identifier + " has invalid size of:" + frameSize);
346
else if (frameSize == 0)
348
//We dont process this frame or add to framemap becuase contains no useful information
349
logger.warning("Empty Frame:" + identifier);
350
throw new EmptyFrameException(identifier + " is empty frame");
352
else if (frameSize > byteBuffer.remaining())
354
logger.warning("Invalid Frame size larger than size before mp3 audio:" + identifier);
355
throw new InvalidFrameException(identifier + " is invalid frame");
359
logger.fine("Frame Size Is:" + frameSize);
360
//Convert v2.2 to v2.4 id just for reading the data
361
String id = ID3Tags.convertFrameID22To24(identifier);
364
//OK,it may be convertable to a v.3 id even though not valid v.4
365
id = ID3Tags.convertFrameID22To23(identifier);
368
// Is it a valid v22 identifier so should be able to find a
369
// frame body for it.
370
if (ID3Tags.isID3v22FrameIdentifier(identifier))
374
// Unknown so will be created as FrameBodyUnsupported
381
logger.fine("Identifier was:" + identifier + " reading using:" + id);
383
//Create Buffer that only contains the body of this frame rather than the remainder of tag
384
ByteBuffer frameBodyBuffer = byteBuffer.slice();
385
frameBodyBuffer.limit(frameSize);
389
frameBody = readBody(id, frameBodyBuffer, frameSize);
393
//Update position of main buffer, so no attempt is made to reread these bytes
394
byteBuffer.position(byteBuffer.position() + frameSize);
400
* Read Frame Size, which has to be decoded
404
private int decodeSize(byte[] buffer)
406
BigInteger bi = new BigInteger(buffer);
407
int tmpSize = bi.intValue();
410
logger.warning("Invalid Frame Size of:" + tmpSize + "Decoded from bin:" + Integer.toBinaryString(tmpSize) + "Decoded from hex:" + Integer.toHexString(tmpSize));
417
* Write Frame raw data
419
* @throws IOException
421
public void write(ByteArrayOutputStream tagBuffer)
423
logger.info("Write Frame to Buffer" + getIdentifier());
424
//This is where we will write header, move position to where we can
426
ByteBuffer headerBuffer = ByteBuffer.allocate(getFrameHeaderSize());
428
//Write Frame Body Data
429
ByteArrayOutputStream bodyOutputStream = new ByteArrayOutputStream();
430
((AbstractID3v2FrameBody) frameBody).write(bodyOutputStream);
433
//Write Frame ID must adjust can only be 3 bytes long
434
headerBuffer.put(Utils.getDefaultBytes(getIdentifier(), "ISO-8859-1"), 0, getFrameIdSize());
435
encodeSize(headerBuffer, frameBody.getSize());
437
//Add header to the Byte Array Output Stream
440
tagBuffer.write(headerBuffer.array());
442
//Add body to the Byte Array Output Stream
443
tagBuffer.write(bodyOutputStream.toByteArray());
445
catch (IOException ioe)
447
//This could never happen coz not writing to file, so convert to RuntimeException
448
throw new RuntimeException(ioe);
453
* Write Frame Size (can now be accurately calculated, have to convert 4 byte int
455
* @param headerBuffer
458
private void encodeSize(ByteBuffer headerBuffer, int size)
460
headerBuffer.put((byte) ((size & 0x00FF0000) >> 16));
461
headerBuffer.put((byte) ((size & 0x0000FF00) >> 8));
462
headerBuffer.put((byte) (size & 0x000000FF));
463
logger.fine("Frame Size Is Actual:" + size + ":Encoded bin:" + Integer.toBinaryString(size) + ":Encoded Hex" + Integer.toHexString(size));
467
* Does the frame identifier meet the syntax for a idv3v2 frame identifier.
468
* must start with a capital letter and only contain capital letters and numbers
473
public boolean isValidID3v2FrameIdentifier(String identifier)
475
Matcher m = ID3v22Frame.validFrameIdentifier.matcher(identifier);
480
* Return String Representation of body
482
public void createStructure()
484
MP3File.getStructureFormatter().openHeadingElement(TYPE_FRAME, getIdentifier());
485
MP3File.getStructureFormatter().addElement(TYPE_FRAME_SIZE, frameSize);
486
frameBody.createStructure();
487
MP3File.getStructureFormatter().closeHeadingElement(TYPE_FRAME);
491
* @return true if considered a common frame
493
public boolean isCommon()
495
return ID3v22Frames.getInstanceOf().isCommon(getId());
499
* @return true if considered a common frame
501
public boolean isBinary()
503
return ID3v22Frames.getInstanceOf().isBinary(getId());
507
* Sets the charset encoding used by the field.
509
* @param encoding charset.
511
public void setEncoding(String encoding)
513
Integer encodingId = TextEncoding.getInstanceOf().getIdForValue(encoding);
518
this.getBody().setTextEncoding(encodingId.byteValue());