diff --git a/taglib/mp4/mp4atom.h b/taglib/mp4/mp4atom.h index 2c0de3d2..ea5091a8 100644 --- a/taglib/mp4/mp4atom.h +++ b/taglib/mp4/mp4atom.h @@ -1,5 +1,5 @@ /************************************************************************** - copyright : (C) 2007 by Lukáš Lalinský + copyright : (C) 2007,2011 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ @@ -40,6 +40,40 @@ namespace TagLib { class Atom; typedef TagLib::List AtomList; + enum AtomDataType + { + TypeImplicit = 0, // for use with tags for which no type needs to be indicated because only one type is allowed + TypeUTF8 = 1, // without any count or null terminator + TypeUTF16 = 2, // also known as UTF-16BE + TypeSJIS = 3, // deprecated unless it is needed for special Japanese characters + TypeHTML = 6, // the HTML file header specifies which HTML version + TypeXML = 7, // the XML header must identify the DTD or schemas + TypeUUID = 8, // also known as GUID; stored as 16 bytes in binary (valid as an ID) + TypeISRC = 9, // stored as UTF-8 text (valid as an ID) + TypeMI3P = 10, // stored as UTF-8 text (valid as an ID) + TypeGIF = 12, // (deprecated) a GIF image + TypeJPEG = 13, // a JPEG image + TypePNG = 14, // a PNG image + TypeURL = 15, // absolute, in UTF-8 characters + TypeDuration = 16, // in milliseconds, 32-bit integer + TypeDateTime = 17, // in UTC, counting seconds since midnight, January 1, 1904; 32 or 64-bits + TypeGenred = 18, // a list of enumerated values + TypeInteger = 21, // a signed big-endian integer with length one of { 1,2,3,4,8 } bytes + TypeRIAAPA = 24, // RIAA parental advisory; { -1=no, 1=yes, 0=unspecified }, 8-bit ingteger + TypeUPC = 25, // Universal Product Code, in text UTF-8 format (valid as an ID) + TypeBMP = 27, // Windows bitmap image + TypeUndefined = 255 // undefined + }; + + struct AtomData { + AtomData(AtomDataType type, ByteVector data) : type(type), locale(0), data(data) {} + AtomDataType type; + int locale; + ByteVector data; + }; + + typedef TagLib::List AtomDataList; + class Atom { public: diff --git a/taglib/mp4/mp4coverart.h b/taglib/mp4/mp4coverart.h index 26c4f9d9..6ba06e7b 100644 --- a/taglib/mp4/mp4coverart.h +++ b/taglib/mp4/mp4coverart.h @@ -29,6 +29,7 @@ #include "tlist.h" #include "tbytevector.h" #include "taglib_export.h" +#include "mp4atom.h" namespace TagLib { @@ -41,8 +42,9 @@ namespace TagLib { * This describes the image type. */ enum Format { - JPEG = 0x0D, - PNG = 0x0E + JPEG = TypeJPEG, + PNG = TypePNG, + BMP = TypeBMP }; CoverArt(Format format, const ByteVector &data); diff --git a/taglib/mp4/mp4item.cpp b/taglib/mp4/mp4item.cpp index 876fac4d..af2cc65c 100644 --- a/taglib/mp4/mp4item.cpp +++ b/taglib/mp4/mp4item.cpp @@ -36,9 +36,10 @@ using namespace TagLib; class MP4::Item::ItemPrivate : public RefCounter { public: - ItemPrivate() : RefCounter(), valid(true) {} + ItemPrivate() : RefCounter(), valid(true), atomDataType(TypeUndefined) {} bool valid; + AtomDataType atomDataType; union { bool m_bool; int m_int; @@ -48,6 +49,7 @@ public: long long m_longlong; }; StringList m_stringList; + ByteVectorList m_byteVectorList; MP4::CoverArtList m_coverArtList; }; @@ -117,6 +119,12 @@ MP4::Item::Item(int value1, int value2) d->m_intPair.second = value2; } +MP4::Item::Item(const ByteVectorList &value) +{ + d = new ItemPrivate; + d->m_byteVectorList = value; +} + MP4::Item::Item(const StringList &value) { d = new ItemPrivate; @@ -129,6 +137,16 @@ MP4::Item::Item(const MP4::CoverArtList &value) d->m_coverArtList = value; } +void MP4::Item::setAtomDataType(MP4::AtomDataType type) +{ + d->atomDataType = type; +} + +MP4::AtomDataType MP4::Item::atomDataType() const +{ + return d->atomDataType; +} + bool MP4::Item::toBool() const { @@ -171,6 +189,12 @@ MP4::Item::toStringList() const return d->m_stringList; } +ByteVectorList +MP4::Item::toByteVectorList() const +{ + return d->m_byteVectorList; +} + MP4::CoverArtList MP4::Item::toCoverArtList() const { diff --git a/taglib/mp4/mp4item.h b/taglib/mp4/mp4item.h index 243a0998..be7aa1a1 100644 --- a/taglib/mp4/mp4item.h +++ b/taglib/mp4/mp4item.h @@ -53,8 +53,12 @@ namespace TagLib { Item(bool value); Item(int first, int second); Item(const StringList &value); + Item(const ByteVectorList &value); Item(const CoverArtList &value); + void setAtomDataType(AtomDataType type); + AtomDataType atomDataType() const; + int toInt() const; uchar toByte() const; uint toUInt() const; @@ -62,6 +66,7 @@ namespace TagLib { bool toBool() const; IntPair toIntPair() const; StringList toStringList() const; + ByteVectorList toByteVectorList() const; CoverArtList toCoverArtList() const; bool isValid() const; diff --git a/taglib/mp4/mp4tag.cpp b/taglib/mp4/mp4tag.cpp index 8d2283ab..0b427bd3 100644 --- a/taglib/mp4/mp4tag.cpp +++ b/taglib/mp4/mp4tag.cpp @@ -1,5 +1,5 @@ /************************************************************************** - copyright : (C) 2007 by Lukáš Lalinský + copyright : (C) 2007,2011 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ @@ -105,10 +105,10 @@ MP4::Tag::~Tag() delete d; } -ByteVectorList -MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm) +MP4::AtomDataList +MP4::Tag::parseData2(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm) { - ByteVectorList result; + AtomDataList result; ByteVector data = file->readBlock(atom->length - 8); int i = 0; unsigned int pos = 0; @@ -125,7 +125,7 @@ MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\""); return result; } - result.append(data.mid(pos + 12, length - 12)); + result.append(AtomData(AtomDataType(flags), data.mid(pos + 12, length - 12))); } else { if(name != "data") { @@ -133,7 +133,7 @@ MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool return result; } if(expectedFlags == -1 || flags == expectedFlags) { - result.append(data.mid(pos + 16, length - 16)); + result.append(AtomData(AtomDataType(flags), data.mid(pos + 16, length - 16))); } } pos += length; @@ -142,6 +142,17 @@ MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool return result; } +ByteVectorList +MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm) +{ + AtomDataList data = parseData2(atom, file, expectedFlags, freeForm); + ByteVectorList result; + for(uint i = 0; i < data.size(); i++) { + result.append(data[i].data); + } + return result; +} + void MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file) { @@ -227,14 +238,34 @@ MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags) void MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file) { - ByteVectorList data = parseData(atom, file, 1, true); + AtomDataList data = parseData2(atom, file, -1, true); if(data.size() > 2) { - StringList value; - for(unsigned int i = 2; i < data.size(); i++) { - value.append(String(data[i], String::UTF8)); + String name = "----:" + String(data[0].data, String::UTF8) + ':' + String(data[1].data, String::UTF8); + AtomDataType type = data[2].type; + for(uint i = 2; i < data.size(); i++) { + if(data[i].type != type) { + debug("MP4: We currently don't support values with multiple types"); + break; + } + } + if(type == TypeUTF8) { + StringList value; + for(uint i = 2; i < data.size(); i++) { + value.append(String(data[i].data, String::UTF8)); + } + Item item(value); + item.setAtomDataType(type); + d->items.insert(name, item); + } + else { + ByteVectorList value; + for(uint i = 2; i < data.size(); i++) { + value.append(data[i].data); + } + Item item(value); + item.setAtomDataType(type); + d->items.insert(name, item); } - String name = "----:" + data[0] + ':' + data[1]; - d->items.insert(name, value); } } @@ -252,7 +283,7 @@ MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file) debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\""); break; } - if(flags == MP4::CoverArt::PNG || flags == MP4::CoverArt::JPEG) { + if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP) { value.append(MP4::CoverArt(MP4::CoverArt::Format(flags), data.mid(pos + 16, length - 16))); } @@ -292,7 +323,7 @@ MP4::Tag::renderBool(const ByteVector &name, MP4::Item &item) { ByteVectorList data; data.append(ByteVector(1, item.toBool() ? '\1' : '\0')); - return renderData(name, 0x15, data); + return renderData(name, TypeInteger, data); } ByteVector @@ -300,7 +331,7 @@ MP4::Tag::renderInt(const ByteVector &name, MP4::Item &item) { ByteVectorList data; data.append(ByteVector::fromShort(item.toInt())); - return renderData(name, 0x15, data); + return renderData(name, TypeInteger, data); } ByteVector @@ -308,7 +339,7 @@ MP4::Tag::renderUInt(const ByteVector &name, MP4::Item &item) { ByteVectorList data; data.append(ByteVector::fromUInt(item.toUInt())); - return renderData(name, 0x15, data); + return renderData(name, TypeInteger, data); } ByteVector @@ -316,7 +347,7 @@ MP4::Tag::renderLongLong(const ByteVector &name, MP4::Item &item) { ByteVectorList data; data.append(ByteVector::fromLongLong(item.toLongLong())); - return renderData(name, 0x15, data); + return renderData(name, TypeInteger, data); } ByteVector @@ -324,7 +355,7 @@ MP4::Tag::renderByte(const ByteVector &name, MP4::Item &item) { ByteVectorList data; data.append(ByteVector(1, item.toByte())); - return renderData(name, 0x15, data); + return renderData(name, TypeInteger, data); } ByteVector @@ -335,7 +366,7 @@ MP4::Tag::renderIntPair(const ByteVector &name, MP4::Item &item) ByteVector::fromShort(item.toIntPair().first) + ByteVector::fromShort(item.toIntPair().second) + ByteVector(2, '\0')); - return renderData(name, 0x00, data); + return renderData(name, TypeImplicit, data); } ByteVector @@ -345,7 +376,7 @@ MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, MP4::Item &item) data.append(ByteVector(2, '\0') + ByteVector::fromShort(item.toIntPair().first) + ByteVector::fromShort(item.toIntPair().second)); - return renderData(name, 0x00, data); + return renderData(name, TypeImplicit, data); } ByteVector @@ -382,9 +413,26 @@ MP4::Tag::renderFreeForm(const String &name, MP4::Item &item) ByteVector data; data.append(renderAtom("mean", ByteVector::fromUInt(0) + header[1].data(String::UTF8))); data.append(renderAtom("name", ByteVector::fromUInt(0) + header[2].data(String::UTF8))); - StringList value = item.toStringList(); - for(unsigned int i = 0; i < value.size(); i++) { - data.append(renderAtom("data", ByteVector::fromUInt(1) + ByteVector(4, '\0') + value[i].data(String::UTF8))); + AtomDataType type = item.atomDataType(); + if(type == TypeUndefined) { + if(!item.toStringList().isEmpty()) { + type = TypeUTF8; + } + else { + type = TypeImplicit; + } + } + if(type == TypeUTF8) { + StringList value = item.toStringList(); + for(unsigned int i = 0; i < value.size(); i++) { + data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + value[i].data(String::UTF8))); + } + } + else { + ByteVectorList value = item.toByteVectorList(); + for(unsigned int i = 0; i < value.size(); i++) { + data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + value[i])); + } } return renderAtom("----", data); } diff --git a/taglib/mp4/mp4tag.h b/taglib/mp4/mp4tag.h index 433edf8a..b5ea6ebb 100644 --- a/taglib/mp4/mp4tag.h +++ b/taglib/mp4/mp4tag.h @@ -1,5 +1,5 @@ /************************************************************************** - copyright : (C) 2007 by Lukáš Lalinský + copyright : (C) 2007,2011 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ @@ -68,6 +68,7 @@ namespace TagLib { ItemListMap &itemListMap(); private: + AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); void parseText(Atom *atom, TagLib::File *file, int expectedFlags = 1); void parseFreeForm(Atom *atom, TagLib::File *file); @@ -83,7 +84,7 @@ namespace TagLib { TagLib::ByteVector padIlst(const ByteVector &data, int length = -1); TagLib::ByteVector renderAtom(const ByteVector &name, const TagLib::ByteVector &data); TagLib::ByteVector renderData(const ByteVector &name, int flags, const TagLib::ByteVectorList &data); - TagLib::ByteVector renderText(const ByteVector &name, Item &item, int flags = 1); + TagLib::ByteVector renderText(const ByteVector &name, Item &item, int flags = TypeUTF8); TagLib::ByteVector renderFreeForm(const String &name, Item &item); TagLib::ByteVector renderBool(const ByteVector &name, Item &item); TagLib::ByteVector renderInt(const ByteVector &name, Item &item);