Mostly get ID3v2.2 working. Sure, it's a standard from 1998 that was

replaced in 1999, but apparently iTunes still uses it.

This gets the frame parsing code to support 2.2 and required deprecating
a couple of methods so that an ID3v2 version could be passed along.

There still needs to be a translation table added to handle converting
ID3v2.2 frame names to their ID3v2.4 equivalents, but that can come later.
For now the framelister program from the examples dir is able to read all
of the text frames.

CCMAIL:Keith Brady <keith@funnelboy.org>


git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@289567 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
This commit is contained in:
Scott Wheeler 2004-02-20 01:41:25 +00:00
parent d21fa22260
commit 619ef906cd
9 changed files with 190 additions and 50 deletions

View File

@ -35,6 +35,6 @@ int main(int argc, char *argv[])
std::cout << "******************** Stripping ID3v1 Tag From: \"" << argv[i] << "\"********************" << std::endl;
MPEG::File f(argv[i]);
f.strip(MPEG::File::ID3v1);
f.strip();
}
}

View File

@ -43,14 +43,14 @@ public:
CommentsFrame::CommentsFrame(String::Type encoding) : Frame("COMM")
{
d = new CommentsFramePrivate();
d = new CommentsFramePrivate;
d->textEncoding = encoding;
}
CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data)
{
d = new CommentsFramePrivate();
parseFields(data.mid(Header::size(), size()));
d = new CommentsFramePrivate;
parseFields(data.mid(Header::size(header()->version()), size()));
}
CommentsFrame::~CommentsFrame()
@ -148,5 +148,5 @@ ByteVector CommentsFrame::renderFields() const
CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new CommentsFramePrivate();
parseFields(data.mid(Header::size(), size()));
parseFields(data.mid(Header::size(h->version()), size()));
}

View File

@ -146,5 +146,5 @@ ByteVector TextIdentificationFrame::renderFields() const
TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new TextIdentificationFramePrivate;
parseFields(data.mid(Header::size(), size()));
parseFields(data.mid(Header::size(h->version()), size()));
}

View File

@ -50,6 +50,11 @@ TagLib::uint Frame::headerSize()
return Header::size();
}
TagLib::uint Frame::headerSize(uint version)
{
return Header::size(version);
}
ByteVector Frame::textDelimiter(String::Type t)
{
ByteVector d = char(0);
@ -108,13 +113,13 @@ ByteVector Frame::render() const
Frame::Frame(const ByteVector &data)
{
d = new FramePrivate();
d = new FramePrivate;
d->header = new Header(data);
}
Frame::Frame(Header *h)
{
d = new FramePrivate();
d = new FramePrivate;
d->header = h;
}
@ -139,7 +144,8 @@ void Frame::parse(const ByteVector &data)
d->header = new Header(data);
// size() is the lenght of the field data
parseFields(data.mid(Header::size(), size()));
parseFields(data.mid(Header::size(d->header->version()), size()));
}
////////////////////////////////////////////////////////////////////////////////
@ -149,11 +155,11 @@ void Frame::parse(const ByteVector &data)
class Frame::Header::HeaderPrivate
{
public:
HeaderPrivate() : frameSize(0) {}
HeaderPrivate() : frameSize(0), version(4) {}
ByteVector frameID;
uint frameSize;
static const unsigned int size = 10;
uint version;
};
////////////////////////////////////////////////////////////////////////////////
@ -162,7 +168,21 @@ public:
TagLib::uint Frame::Header::size()
{
return HeaderPrivate::size;
return size(4);
}
TagLib::uint Frame::Header::size(uint version)
{
switch(version) {
case 0:
case 1:
case 2:
return 6;
case 3:
case 4:
default:
return 10;
}
}
////////////////////////////////////////////////////////////////////////////////
@ -175,6 +195,12 @@ Frame::Header::Header(const ByteVector &data, bool synchSafeInts)
setData(data, synchSafeInts);
}
Frame::Header::Header(const ByteVector &data, uint version)
{
d = new HeaderPrivate;
setData(data, version);
}
Frame::Header::~Header()
{
delete d;
@ -182,33 +208,78 @@ Frame::Header::~Header()
void Frame::Header::setData(const ByteVector &data, bool synchSafeInts)
{
if(data.size() < 4) {
debug("You must at least specify a frame ID.");
return;
setData(data, uint(synchSafeInts ? 4 : 3));
}
void Frame::Header::setData(const ByteVector &data, uint version)
{
d->version = version;
switch(version) {
case 0:
case 1:
case 2:
{
// ID3v2.2
if(data.size() < 3) {
debug("You must at least specify a frame ID.");
return;
}
// Set the frame ID -- the first three bytes
d->frameID = data.mid(0, 3);
// If the full header information was not passed in, do not continue to the
// steps to parse the frame size and flags.
if(data.size() < 6) {
d->frameSize = 0;
return;
}
d->frameSize = data.mid(3, 3).toUInt();
break;
}
case 3:
case 4:
default:
{
// ID3v2.3 / ID3v2.4
// set the frame ID -- the first four bytes
if(data.size() < 4) {
debug("You must at least specify a frame ID.");
return;
}
d->frameID = data.mid(0, 4);
// Set the frame ID -- the first four bytes
// If the full header information was not passed in, do not continue to the
// steps to parse the frame size and flags.
d->frameID = data.mid(0, 4);
if(data.size() < 10) {
d->frameSize = 0;
return;
// If the full header information was not passed in, do not continue to the
// steps to parse the frame size and flags.
if(data.size() < 10) {
d->frameSize = 0;
return;
}
// Set the size -- the frame size is the four bytes starting at byte four in
// the frame header (structure 4)
if(version >= 4)
d->frameSize = SynchData::toUInt(data.mid(4, 4));
else
d->frameSize = data.mid(4, 4).toUInt();
// TODO: read flags
break;
}
}
// Set the size -- the frame size is the four bytes starting at byte four in
// the frame header (structure 4)
if(synchSafeInts)
d->frameSize = SynchData::toUInt(data.mid(4, 4));
else
d->frameSize = data.mid(4, 4).toUInt();
// read flags
// ...
}
ByteVector Frame::Header::frameID() const
@ -231,6 +302,11 @@ void Frame::Header::setFrameSize(uint size)
d->frameSize = size;
}
TagLib::uint Frame::Header::version() const
{
return d->version;
}
ByteVector Frame::Header::render() const
{
ByteVector flags(2, char(0)); // just blank for the moment

View File

@ -65,8 +65,20 @@ namespace TagLib {
/*!
* Returns the size of the frame header
*
* \deprecated This is only accurate for ID3v2.3 or ID3v2.4. Please use
* the call below which accepts an ID3v2 version number. In the next
* non-binary compatible release this will be made into a non-static
* member that checks the internal ID3v2 version.
*/
static uint headerSize();
static uint headerSize(); // BIC: remove and make non-static
/*!
* Returns the size of the frame header for the given ID3v2 version.
*
* \deprecated Please see the explanation above.
*/
static uint headerSize(uint version); // BIC: remove and make non-static
/*!
* Sets the data that will be used as the frame. Since the length is not
@ -187,8 +199,20 @@ namespace TagLib {
* Construct a Frame Header based on \a data. \a data must at least
* contain a 4 byte frame ID, and optionally can contain flag data and the
* frame size. i.e. Just the frame id -- "TALB" -- is a valid value.
*
* \deprecated Please use the constructor below that accepts a version
* number.
*/
explicit Header(const ByteVector &data, bool synchSafeInts = true);
explicit Header(const ByteVector &data, bool synchSafeInts);
/*!
* Construct a Frame Header based on \a data. \a data must at least
* contain a 4 byte frame ID, and optionally can contain flag data and the
* frame size. i.e. Just the frame id -- "TALB" -- is a valid value.
*
* \a version should be the ID3v2 version of the tag.
*/
explicit Header(const ByteVector &data, uint version = 4);
/*!
* Destroys this Header instance.
@ -197,8 +221,17 @@ namespace TagLib {
/*!
* Sets the data for the Header.
*
* \deprecated Please use the version below that accepts an ID3v2 version
* number.
*/
void setData(const ByteVector &data, bool synchSafeInts = true);
void setData(const ByteVector &data, bool synchSafeInts);
/*!
* Sets the data for the Header. \a version should indicate the ID3v2
* version number of the tag that this frame is contained in.
*/
void setData(const ByteVector &data, uint version = 4);
/*!
* Returns the Frame ID (Structure, <a href="id3v2-structure.html#4">4</a>)
@ -228,11 +261,29 @@ namespace TagLib {
void setFrameSize(uint size);
/*!
* Returns the size of the frame header in bytes. Currently this is
* always 10.
* Returns the ID3v2 version of the header (as passed in from the
* construction of the header).
*/
uint version() const;
/*!
* Returns the size of the frame header in bytes.
*
* \deprecated Please use the version of this method that accepts a
* version. This is only accurate for ID3v2.3 and ID3v2.4. This will be
* removed in the next binary incompatible release (2.0) and will be
* replaced with a non-static method that checks the frame version.
*/
static uint size();
/*!
* Returns the size of the frame header in bytes for the ID3v2 version
* that's given.
*
* \deprecated Please see the explanation in the version above.
*/
static uint size(uint version);
/*!
* Render the Header back to binary format in a ByteVector.
*/

View File

@ -56,15 +56,20 @@ FrameFactory *FrameFactory::instance()
Frame *FrameFactory::createFrame(const ByteVector &data, bool synchSafeInts) const
{
Frame::Header *header = new Frame::Header(data, synchSafeInts);
return createFrame(data, uint(synchSafeInts ? 4 : 3));
}
Frame *FrameFactory::createFrame(const ByteVector &data, uint version) const
{
Frame::Header *header = new Frame::Header(data, version);
TagLib::ByteVector frameID = header->frameID();
// A quick sanity check -- make sure that the frameID is 4 uppercase Latin1
// characters. Also make sure that there is data in the frame.
if(!frameID.size() == 4 || header->frameSize() <= 0)
return 0;
if(!frameID.size() == (version < 3 ? 3 : 4) || header->frameSize() <= 0)
return 0;
for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {

View File

@ -1,4 +1,4 @@
/***************************************************************************
/***************************************************************************
copyright : (C) 2002, 2003 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
@ -56,8 +56,18 @@ namespace TagLib {
* Create a frame based on \a data. \a synchSafeInts should only be set
* false if we are parsing an old tag (v2.3 or older) that does not support
* synchsafe ints.
*
* \deprecated Please use the method below that accepts an ID3 version
* number in new code.
*/
Frame *createFrame(const ByteVector &data, bool synchSafeInts = true) const;
Frame *createFrame(const ByteVector &data, bool synchSafeInts) const;
/*!
* Create a frame based on \a data. \a version should indicate the ID3v2
* version of the tag. As ID3v2.4 is the most current version of the
* standard 4 is the default.
*/
Frame *createFrame(const ByteVector &data, uint version = 4) const;
/*!
* Returns the default text encoding for text frames. If setTextEncoding()

View File

@ -78,12 +78,12 @@ ByteVector Header::fileIdentifier()
Header::Header()
{
d = new HeaderPrivate();
d = new HeaderPrivate;
}
Header::Header(const ByteVector &data)
{
d = new HeaderPrivate();
d = new HeaderPrivate;
parse(data);
}

View File

@ -365,7 +365,7 @@ void ID3v2::Tag::parse(const ByteVector &data)
// Make sure that there is at least enough room in the remaining frame data for
// a frame header.
while(frameDataPosition < frameDataLength - Frame::headerSize()) {
while(frameDataPosition < frameDataLength - Frame::headerSize(d->header.majorVersion())) {
// If the next data is position is 0, assume that we've hit the padding
// portion of the frame data.
@ -379,10 +379,8 @@ void ID3v2::Tag::parse(const ByteVector &data)
return;
}
bool synchSafeInts = d->header.majorVersion() >= 4;
Frame *frame = d->factory->createFrame(data.mid(frameDataOffset + frameDataPosition),
synchSafeInts);
d->header.majorVersion());
if(!frame)
return;
@ -394,7 +392,7 @@ void ID3v2::Tag::parse(const ByteVector &data)
return;
}
frameDataPosition += frame->size() + Frame::headerSize();
frameDataPosition += frame->size() + Frame::headerSize(d->header.majorVersion());
addFrame(frame);
}
}