Home | History | Annotate | Download | only in id3
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "ID3"
     19 #include <utils/Log.h>
     20 
     21 #include "../include/ID3.h"
     22 
     23 #include <media/stagefright/foundation/ADebug.h>
     24 #include <media/stagefright/DataSource.h>
     25 #include <media/stagefright/Utils.h>
     26 #include <utils/String8.h>
     27 #include <byteswap.h>
     28 
     29 namespace android {
     30 
     31 static const size_t kMaxMetadataSize = 3 * 1024 * 1024;
     32 
     33 struct MemorySource : public DataSource {
     34     MemorySource(const uint8_t *data, size_t size)
     35         : mData(data),
     36           mSize(size) {
     37     }
     38 
     39     virtual status_t initCheck() const {
     40         return OK;
     41     }
     42 
     43     virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
     44         off64_t available = (offset >= mSize) ? 0ll : mSize - offset;
     45 
     46         size_t copy = (available > size) ? size : available;
     47         memcpy(data, mData + offset, copy);
     48 
     49         return copy;
     50     }
     51 
     52 private:
     53     const uint8_t *mData;
     54     size_t mSize;
     55 
     56     DISALLOW_EVIL_CONSTRUCTORS(MemorySource);
     57 };
     58 
     59 ID3::ID3(const sp<DataSource> &source, bool ignoreV1)
     60     : mIsValid(false),
     61       mData(NULL),
     62       mSize(0),
     63       mFirstFrameOffset(0),
     64       mVersion(ID3_UNKNOWN),
     65       mRawSize(0) {
     66     mIsValid = parseV2(source);
     67 
     68     if (!mIsValid && !ignoreV1) {
     69         mIsValid = parseV1(source);
     70     }
     71 }
     72 
     73 ID3::ID3(const uint8_t *data, size_t size, bool ignoreV1)
     74     : mIsValid(false),
     75       mData(NULL),
     76       mSize(0),
     77       mFirstFrameOffset(0),
     78       mVersion(ID3_UNKNOWN),
     79       mRawSize(0) {
     80     sp<MemorySource> source = new MemorySource(data, size);
     81 
     82     mIsValid = parseV2(source);
     83 
     84     if (!mIsValid && !ignoreV1) {
     85         mIsValid = parseV1(source);
     86     }
     87 }
     88 
     89 ID3::~ID3() {
     90     if (mData) {
     91         free(mData);
     92         mData = NULL;
     93     }
     94 }
     95 
     96 bool ID3::isValid() const {
     97     return mIsValid;
     98 }
     99 
    100 ID3::Version ID3::version() const {
    101     return mVersion;
    102 }
    103 
    104 // static
    105 bool ID3::ParseSyncsafeInteger(const uint8_t encoded[4], size_t *x) {
    106     *x = 0;
    107     for (int32_t i = 0; i < 4; ++i) {
    108         if (encoded[i] & 0x80) {
    109             return false;
    110         }
    111 
    112         *x = ((*x) << 7) | encoded[i];
    113     }
    114 
    115     return true;
    116 }
    117 
    118 bool ID3::parseV2(const sp<DataSource> &source) {
    119 struct id3_header {
    120     char id[3];
    121     uint8_t version_major;
    122     uint8_t version_minor;
    123     uint8_t flags;
    124     uint8_t enc_size[4];
    125     };
    126 
    127     id3_header header;
    128     if (source->readAt(
    129                 0, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
    130         return false;
    131     }
    132 
    133     if (memcmp(header.id, "ID3", 3)) {
    134         return false;
    135     }
    136 
    137     if (header.version_major == 0xff || header.version_minor == 0xff) {
    138         return false;
    139     }
    140 
    141     if (header.version_major == 2) {
    142         if (header.flags & 0x3f) {
    143             // We only support the 2 high bits, if any of the lower bits are
    144             // set, we cannot guarantee to understand the tag format.
    145             return false;
    146         }
    147 
    148         if (header.flags & 0x40) {
    149             // No compression scheme has been decided yet, ignore the
    150             // tag if compression is indicated.
    151 
    152             return false;
    153         }
    154     } else if (header.version_major == 3) {
    155         if (header.flags & 0x1f) {
    156             // We only support the 3 high bits, if any of the lower bits are
    157             // set, we cannot guarantee to understand the tag format.
    158             return false;
    159         }
    160     } else if (header.version_major == 4) {
    161         if (header.flags & 0x0f) {
    162             // The lower 4 bits are undefined in this spec.
    163             return false;
    164         }
    165     } else {
    166         return false;
    167     }
    168 
    169     size_t size;
    170     if (!ParseSyncsafeInteger(header.enc_size, &size)) {
    171         return false;
    172     }
    173 
    174     if (size > kMaxMetadataSize) {
    175         ALOGE("skipping huge ID3 metadata of size %d", size);
    176         return false;
    177     }
    178 
    179     mData = (uint8_t *)malloc(size);
    180 
    181     if (mData == NULL) {
    182         return false;
    183     }
    184 
    185     mSize = size;
    186     mRawSize = mSize + sizeof(header);
    187 
    188     if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
    189         free(mData);
    190         mData = NULL;
    191 
    192         return false;
    193     }
    194 
    195     if (header.version_major == 4) {
    196         void *copy = malloc(size);
    197         memcpy(copy, mData, size);
    198 
    199         bool success = removeUnsynchronizationV2_4(false /* iTunesHack */);
    200         if (!success) {
    201             memcpy(mData, copy, size);
    202             mSize = size;
    203 
    204             success = removeUnsynchronizationV2_4(true /* iTunesHack */);
    205 
    206             if (success) {
    207                 ALOGV("Had to apply the iTunes hack to parse this ID3 tag");
    208             }
    209         }
    210 
    211         free(copy);
    212         copy = NULL;
    213 
    214         if (!success) {
    215             free(mData);
    216             mData = NULL;
    217 
    218             return false;
    219         }
    220     } else if (header.flags & 0x80) {
    221         ALOGV("removing unsynchronization");
    222 
    223         removeUnsynchronization();
    224     }
    225 
    226     mFirstFrameOffset = 0;
    227     if (header.version_major == 3 && (header.flags & 0x40)) {
    228         // Version 2.3 has an optional extended header.
    229 
    230         if (mSize < 4) {
    231             free(mData);
    232             mData = NULL;
    233 
    234             return false;
    235         }
    236 
    237         size_t extendedHeaderSize = U32_AT(&mData[0]) + 4;
    238 
    239         if (extendedHeaderSize > mSize) {
    240             free(mData);
    241             mData = NULL;
    242 
    243             return false;
    244         }
    245 
    246         mFirstFrameOffset = extendedHeaderSize;
    247 
    248         uint16_t extendedFlags = 0;
    249         if (extendedHeaderSize >= 6) {
    250             extendedFlags = U16_AT(&mData[4]);
    251 
    252             if (extendedHeaderSize >= 10) {
    253                 size_t paddingSize = U32_AT(&mData[6]);
    254 
    255                 if (mFirstFrameOffset + paddingSize > mSize) {
    256                     free(mData);
    257                     mData = NULL;
    258 
    259                     return false;
    260                 }
    261 
    262                 mSize -= paddingSize;
    263             }
    264 
    265             if (extendedFlags & 0x8000) {
    266                 ALOGV("have crc");
    267             }
    268         }
    269     } else if (header.version_major == 4 && (header.flags & 0x40)) {
    270         // Version 2.4 has an optional extended header, that's different
    271         // from Version 2.3's...
    272 
    273         if (mSize < 4) {
    274             free(mData);
    275             mData = NULL;
    276 
    277             return false;
    278         }
    279 
    280         size_t ext_size;
    281         if (!ParseSyncsafeInteger(mData, &ext_size)) {
    282             free(mData);
    283             mData = NULL;
    284 
    285             return false;
    286         }
    287 
    288         if (ext_size < 6 || ext_size > mSize) {
    289             free(mData);
    290             mData = NULL;
    291 
    292             return false;
    293         }
    294 
    295         mFirstFrameOffset = ext_size;
    296     }
    297 
    298     if (header.version_major == 2) {
    299         mVersion = ID3_V2_2;
    300     } else if (header.version_major == 3) {
    301         mVersion = ID3_V2_3;
    302     } else {
    303         CHECK_EQ(header.version_major, 4);
    304         mVersion = ID3_V2_4;
    305     }
    306 
    307     return true;
    308 }
    309 
    310 void ID3::removeUnsynchronization() {
    311     for (size_t i = 0; i + 1 < mSize; ++i) {
    312         if (mData[i] == 0xff && mData[i + 1] == 0x00) {
    313             memmove(&mData[i + 1], &mData[i + 2], mSize - i - 2);
    314             --mSize;
    315         }
    316     }
    317 }
    318 
    319 static void WriteSyncsafeInteger(uint8_t *dst, size_t x) {
    320     for (size_t i = 0; i < 4; ++i) {
    321         dst[3 - i] = (x & 0x7f);
    322         x >>= 7;
    323     }
    324 }
    325 
    326 bool ID3::removeUnsynchronizationV2_4(bool iTunesHack) {
    327     size_t oldSize = mSize;
    328 
    329     size_t offset = 0;
    330     while (offset + 10 <= mSize) {
    331         if (!memcmp(&mData[offset], "\0\0\0\0", 4)) {
    332             break;
    333         }
    334 
    335         size_t dataSize;
    336         if (iTunesHack) {
    337             dataSize = U32_AT(&mData[offset + 4]);
    338         } else if (!ParseSyncsafeInteger(&mData[offset + 4], &dataSize)) {
    339             return false;
    340         }
    341 
    342         if (offset + dataSize + 10 > mSize) {
    343             return false;
    344         }
    345 
    346         uint16_t flags = U16_AT(&mData[offset + 8]);
    347         uint16_t prevFlags = flags;
    348 
    349         if (flags & 1) {
    350             // Strip data length indicator
    351 
    352             memmove(&mData[offset + 10], &mData[offset + 14], mSize - offset - 14);
    353             mSize -= 4;
    354             dataSize -= 4;
    355 
    356             flags &= ~1;
    357         }
    358 
    359         if (flags & 2) {
    360             // This file has "unsynchronization", so we have to replace occurrences
    361             // of 0xff 0x00 with just 0xff in order to get the real data.
    362 
    363             size_t readOffset = offset + 11;
    364             size_t writeOffset = offset + 11;
    365             for (size_t i = 0; i + 1 < dataSize; ++i) {
    366                 if (mData[readOffset - 1] == 0xff
    367                         && mData[readOffset] == 0x00) {
    368                     ++readOffset;
    369                     --mSize;
    370                     --dataSize;
    371                 }
    372                 mData[writeOffset++] = mData[readOffset++];
    373             }
    374             // move the remaining data following this frame
    375             memmove(&mData[writeOffset], &mData[readOffset], oldSize - readOffset);
    376 
    377             flags &= ~2;
    378         }
    379 
    380         if (flags != prevFlags || iTunesHack) {
    381             WriteSyncsafeInteger(&mData[offset + 4], dataSize);
    382             mData[offset + 8] = flags >> 8;
    383             mData[offset + 9] = flags & 0xff;
    384         }
    385 
    386         offset += 10 + dataSize;
    387     }
    388 
    389     memset(&mData[mSize], 0, oldSize - mSize);
    390 
    391     return true;
    392 }
    393 
    394 ID3::Iterator::Iterator(const ID3 &parent, const char *id)
    395     : mParent(parent),
    396       mID(NULL),
    397       mOffset(mParent.mFirstFrameOffset),
    398       mFrameData(NULL),
    399       mFrameSize(0) {
    400     if (id) {
    401         mID = strdup(id);
    402     }
    403 
    404     findFrame();
    405 }
    406 
    407 ID3::Iterator::~Iterator() {
    408     if (mID) {
    409         free(mID);
    410         mID = NULL;
    411     }
    412 }
    413 
    414 bool ID3::Iterator::done() const {
    415     return mFrameData == NULL;
    416 }
    417 
    418 void ID3::Iterator::next() {
    419     if (mFrameData == NULL) {
    420         return;
    421     }
    422 
    423     mOffset += mFrameSize;
    424 
    425     findFrame();
    426 }
    427 
    428 void ID3::Iterator::getID(String8 *id) const {
    429     id->setTo("");
    430 
    431     if (mFrameData == NULL) {
    432         return;
    433     }
    434 
    435     if (mParent.mVersion == ID3_V2_2) {
    436         id->setTo((const char *)&mParent.mData[mOffset], 3);
    437     } else if (mParent.mVersion == ID3_V2_3 || mParent.mVersion == ID3_V2_4) {
    438         id->setTo((const char *)&mParent.mData[mOffset], 4);
    439     } else {
    440         CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
    441 
    442         switch (mOffset) {
    443             case 3:
    444                 id->setTo("TT2");
    445                 break;
    446             case 33:
    447                 id->setTo("TP1");
    448                 break;
    449             case 63:
    450                 id->setTo("TAL");
    451                 break;
    452             case 93:
    453                 id->setTo("TYE");
    454                 break;
    455             case 97:
    456                 id->setTo("COM");
    457                 break;
    458             case 126:
    459                 id->setTo("TRK");
    460                 break;
    461             case 127:
    462                 id->setTo("TCO");
    463                 break;
    464             default:
    465                 CHECK(!"should not be here.");
    466                 break;
    467         }
    468     }
    469 }
    470 
    471 static void convertISO8859ToString8(
    472         const uint8_t *data, size_t size,
    473         String8 *s) {
    474     size_t utf8len = 0;
    475     for (size_t i = 0; i < size; ++i) {
    476         if (data[i] == '\0') {
    477             size = i;
    478             break;
    479         } else if (data[i] < 0x80) {
    480             ++utf8len;
    481         } else {
    482             utf8len += 2;
    483         }
    484     }
    485 
    486     if (utf8len == size) {
    487         // Only ASCII characters present.
    488 
    489         s->setTo((const char *)data, size);
    490         return;
    491     }
    492 
    493     char *tmp = new char[utf8len];
    494     char *ptr = tmp;
    495     for (size_t i = 0; i < size; ++i) {
    496         if (data[i] == '\0') {
    497             break;
    498         } else if (data[i] < 0x80) {
    499             *ptr++ = data[i];
    500         } else if (data[i] < 0xc0) {
    501             *ptr++ = 0xc2;
    502             *ptr++ = data[i];
    503         } else {
    504             *ptr++ = 0xc3;
    505             *ptr++ = data[i] - 64;
    506         }
    507     }
    508 
    509     s->setTo(tmp, utf8len);
    510 
    511     delete[] tmp;
    512     tmp = NULL;
    513 }
    514 
    515 // the 2nd argument is used to get the data following the \0 in a comment field
    516 void ID3::Iterator::getString(String8 *id, String8 *comment) const {
    517     getstring(id, false);
    518     if (comment != NULL) {
    519         getstring(comment, true);
    520     }
    521 }
    522 
    523 // comment fields (COM/COMM) contain an initial short descriptor, followed by \0,
    524 // followed by more data. The data following the \0 can be retrieved by setting
    525 // "otherdata" to true.
    526 void ID3::Iterator::getstring(String8 *id, bool otherdata) const {
    527     id->setTo("");
    528 
    529     const uint8_t *frameData = mFrameData;
    530     if (frameData == NULL) {
    531         return;
    532     }
    533 
    534     uint8_t encoding = *frameData;
    535 
    536     if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) {
    537         if (mOffset == 126 || mOffset == 127) {
    538             // Special treatment for the track number and genre.
    539             char tmp[16];
    540             sprintf(tmp, "%d", (int)*frameData);
    541 
    542             id->setTo(tmp);
    543             return;
    544         }
    545 
    546         convertISO8859ToString8(frameData, mFrameSize, id);
    547         return;
    548     }
    549 
    550     size_t n = mFrameSize - getHeaderLength() - 1;
    551     if (otherdata) {
    552         // skip past the encoding, language, and the 0 separator
    553         frameData += 4;
    554         int32_t i = n - 4;
    555         while(--i >= 0 && *++frameData != 0) ;
    556         int skipped = (frameData - mFrameData);
    557         if (skipped >= (int)n) {
    558             return;
    559         }
    560         n -= skipped;
    561     }
    562 
    563     if (encoding == 0x00) {
    564         // ISO 8859-1
    565         convertISO8859ToString8(frameData + 1, n, id);
    566     } else if (encoding == 0x03) {
    567         // UTF-8
    568         id->setTo((const char *)(frameData + 1), n);
    569     } else if (encoding == 0x02) {
    570         // UTF-16 BE, no byte order mark.
    571         // API wants number of characters, not number of bytes...
    572         int len = n / 2;
    573         const char16_t *framedata = (const char16_t *) (frameData + 1);
    574         char16_t *framedatacopy = NULL;
    575 #if BYTE_ORDER == LITTLE_ENDIAN
    576         framedatacopy = new char16_t[len];
    577         for (int i = 0; i < len; i++) {
    578             framedatacopy[i] = bswap_16(framedata[i]);
    579         }
    580         framedata = framedatacopy;
    581 #endif
    582         id->setTo(framedata, len);
    583         if (framedatacopy != NULL) {
    584             delete[] framedatacopy;
    585         }
    586     } else {
    587         // UCS-2
    588         // API wants number of characters, not number of bytes...
    589         int len = n / 2;
    590         const char16_t *framedata = (const char16_t *) (frameData + 1);
    591         char16_t *framedatacopy = NULL;
    592         if (*framedata == 0xfffe) {
    593             // endianness marker doesn't match host endianness, convert
    594             framedatacopy = new char16_t[len];
    595             for (int i = 0; i < len; i++) {
    596                 framedatacopy[i] = bswap_16(framedata[i]);
    597             }
    598             framedata = framedatacopy;
    599         }
    600         // If the string starts with an endianness marker, skip it
    601         if (*framedata == 0xfeff) {
    602             framedata++;
    603             len--;
    604         }
    605         id->setTo(framedata, len);
    606         if (framedatacopy != NULL) {
    607             delete[] framedatacopy;
    608         }
    609     }
    610 }
    611 
    612 const uint8_t *ID3::Iterator::getData(size_t *length) const {
    613     *length = 0;
    614 
    615     if (mFrameData == NULL) {
    616         return NULL;
    617     }
    618 
    619     *length = mFrameSize - getHeaderLength();
    620 
    621     return mFrameData;
    622 }
    623 
    624 size_t ID3::Iterator::getHeaderLength() const {
    625     if (mParent.mVersion == ID3_V2_2) {
    626         return 6;
    627     } else if (mParent.mVersion == ID3_V2_3 || mParent.mVersion == ID3_V2_4) {
    628         return 10;
    629     } else {
    630         CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
    631         return 0;
    632     }
    633 }
    634 
    635 void ID3::Iterator::findFrame() {
    636     for (;;) {
    637         mFrameData = NULL;
    638         mFrameSize = 0;
    639 
    640         if (mParent.mVersion == ID3_V2_2) {
    641             if (mOffset + 6 > mParent.mSize) {
    642                 return;
    643             }
    644 
    645             if (!memcmp(&mParent.mData[mOffset], "\0\0\0", 3)) {
    646                 return;
    647             }
    648 
    649             mFrameSize =
    650                 (mParent.mData[mOffset + 3] << 16)
    651                 | (mParent.mData[mOffset + 4] << 8)
    652                 | mParent.mData[mOffset + 5];
    653 
    654             mFrameSize += 6;
    655 
    656             if (mOffset + mFrameSize > mParent.mSize) {
    657                 ALOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
    658                      mOffset, mFrameSize, mParent.mSize - mOffset - 6);
    659                 return;
    660             }
    661 
    662             mFrameData = &mParent.mData[mOffset + 6];
    663 
    664             if (!mID) {
    665                 break;
    666             }
    667 
    668             char id[4];
    669             memcpy(id, &mParent.mData[mOffset], 3);
    670             id[3] = '\0';
    671 
    672             if (!strcmp(id, mID)) {
    673                 break;
    674             }
    675         } else if (mParent.mVersion == ID3_V2_3
    676                 || mParent.mVersion == ID3_V2_4) {
    677             if (mOffset + 10 > mParent.mSize) {
    678                 return;
    679             }
    680 
    681             if (!memcmp(&mParent.mData[mOffset], "\0\0\0\0", 4)) {
    682                 return;
    683             }
    684 
    685             size_t baseSize;
    686             if (mParent.mVersion == ID3_V2_4) {
    687                 if (!ParseSyncsafeInteger(
    688                             &mParent.mData[mOffset + 4], &baseSize)) {
    689                     return;
    690                 }
    691             } else {
    692                 baseSize = U32_AT(&mParent.mData[mOffset + 4]);
    693             }
    694 
    695             mFrameSize = 10 + baseSize;
    696 
    697             if (mOffset + mFrameSize > mParent.mSize) {
    698                 ALOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
    699                      mOffset, mFrameSize, mParent.mSize - mOffset - 10);
    700                 return;
    701             }
    702 
    703             uint16_t flags = U16_AT(&mParent.mData[mOffset + 8]);
    704 
    705             if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000c))
    706                 || (mParent.mVersion == ID3_V2_3 && (flags & 0x00c0))) {
    707                 // Compression or encryption are not supported at this time.
    708                 // Per-frame unsynchronization and data-length indicator
    709                 // have already been taken care of.
    710 
    711                 ALOGV("Skipping unsupported frame (compression, encryption "
    712                      "or per-frame unsynchronization flagged");
    713 
    714                 mOffset += mFrameSize;
    715                 continue;
    716             }
    717 
    718             mFrameData = &mParent.mData[mOffset + 10];
    719 
    720             if (!mID) {
    721                 break;
    722             }
    723 
    724             char id[5];
    725             memcpy(id, &mParent.mData[mOffset], 4);
    726             id[4] = '\0';
    727 
    728             if (!strcmp(id, mID)) {
    729                 break;
    730             }
    731         } else {
    732             CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
    733 
    734             if (mOffset >= mParent.mSize) {
    735                 return;
    736             }
    737 
    738             mFrameData = &mParent.mData[mOffset];
    739 
    740             switch (mOffset) {
    741                 case 3:
    742                 case 33:
    743                 case 63:
    744                     mFrameSize = 30;
    745                     break;
    746                 case 93:
    747                     mFrameSize = 4;
    748                     break;
    749                 case 97:
    750                     if (mParent.mVersion == ID3_V1) {
    751                         mFrameSize = 30;
    752                     } else {
    753                         mFrameSize = 29;
    754                     }
    755                     break;
    756                 case 126:
    757                     mFrameSize = 1;
    758                     break;
    759                 case 127:
    760                     mFrameSize = 1;
    761                     break;
    762                 default:
    763                     CHECK(!"Should not be here, invalid offset.");
    764                     break;
    765             }
    766 
    767             if (!mID) {
    768                 break;
    769             }
    770 
    771             String8 id;
    772             getID(&id);
    773 
    774             if (id == mID) {
    775                 break;
    776             }
    777         }
    778 
    779         mOffset += mFrameSize;
    780     }
    781 }
    782 
    783 static size_t StringSize(const uint8_t *start, uint8_t encoding) {
    784     if (encoding == 0x00 || encoding == 0x03) {
    785         // ISO 8859-1 or UTF-8
    786         return strlen((const char *)start) + 1;
    787     }
    788 
    789     // UCS-2
    790     size_t n = 0;
    791     while (start[n] != '\0' || start[n + 1] != '\0') {
    792         n += 2;
    793     }
    794 
    795     // Add size of null termination.
    796     return n + 2;
    797 }
    798 
    799 const void *
    800 ID3::getAlbumArt(size_t *length, String8 *mime) const {
    801     *length = 0;
    802     mime->setTo("");
    803 
    804     Iterator it(
    805             *this,
    806             (mVersion == ID3_V2_3 || mVersion == ID3_V2_4) ? "APIC" : "PIC");
    807 
    808     while (!it.done()) {
    809         size_t size;
    810         const uint8_t *data = it.getData(&size);
    811 
    812         if (mVersion == ID3_V2_3 || mVersion == ID3_V2_4) {
    813             uint8_t encoding = data[0];
    814             mime->setTo((const char *)&data[1]);
    815             size_t mimeLen = strlen((const char *)&data[1]) + 1;
    816 
    817             uint8_t picType = data[1 + mimeLen];
    818 #if 0
    819             if (picType != 0x03) {
    820                 // Front Cover Art
    821                 it.next();
    822                 continue;
    823             }
    824 #endif
    825 
    826             size_t descLen = StringSize(&data[2 + mimeLen], encoding);
    827 
    828             *length = size - 2 - mimeLen - descLen;
    829 
    830             return &data[2 + mimeLen + descLen];
    831         } else {
    832             uint8_t encoding = data[0];
    833 
    834             if (!memcmp(&data[1], "PNG", 3)) {
    835                 mime->setTo("image/png");
    836             } else if (!memcmp(&data[1], "JPG", 3)) {
    837                 mime->setTo("image/jpeg");
    838             } else if (!memcmp(&data[1], "-->", 3)) {
    839                 mime->setTo("text/plain");
    840             } else {
    841                 return NULL;
    842             }
    843 
    844 #if 0
    845             uint8_t picType = data[4];
    846             if (picType != 0x03) {
    847                 // Front Cover Art
    848                 it.next();
    849                 continue;
    850             }
    851 #endif
    852 
    853             size_t descLen = StringSize(&data[5], encoding);
    854 
    855             *length = size - 5 - descLen;
    856 
    857             return &data[5 + descLen];
    858         }
    859     }
    860 
    861     return NULL;
    862 }
    863 
    864 bool ID3::parseV1(const sp<DataSource> &source) {
    865     const size_t V1_TAG_SIZE = 128;
    866 
    867     off64_t size;
    868     if (source->getSize(&size) != OK || size < (off64_t)V1_TAG_SIZE) {
    869         return false;
    870     }
    871 
    872     mData = (uint8_t *)malloc(V1_TAG_SIZE);
    873     if (source->readAt(size - V1_TAG_SIZE, mData, V1_TAG_SIZE)
    874             != (ssize_t)V1_TAG_SIZE) {
    875         free(mData);
    876         mData = NULL;
    877 
    878         return false;
    879     }
    880 
    881     if (memcmp("TAG", mData, 3)) {
    882         free(mData);
    883         mData = NULL;
    884 
    885         return false;
    886     }
    887 
    888     mSize = V1_TAG_SIZE;
    889     mFirstFrameOffset = 3;
    890 
    891     if (mData[V1_TAG_SIZE - 3] != 0) {
    892         mVersion = ID3_V1;
    893     } else {
    894         mVersion = ID3_V1_1;
    895     }
    896 
    897     return true;
    898 }
    899 
    900 }  // namespace android
    901