ID3.cpp revision 632a7dfdab89ffa79846ad37718d0582fd36affe
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/DataSource.h>
24#include <media/stagefright/MediaDebug.h>
25#include <media/stagefright/Utils.h>
26#include <utils/String8.h>
27#include <byteswap.h>
28
29namespace android {
30
31ID3::ID3(const sp<DataSource> &source)
32    : mIsValid(false),
33      mData(NULL),
34      mSize(0),
35      mFirstFrameOffset(0),
36      mVersion(ID3_UNKNOWN) {
37    mIsValid = parseV2(source);
38
39    if (!mIsValid) {
40        mIsValid = parseV1(source);
41    }
42}
43
44ID3::~ID3() {
45    if (mData) {
46        free(mData);
47        mData = NULL;
48    }
49}
50
51bool ID3::isValid() const {
52    return mIsValid;
53}
54
55ID3::Version ID3::version() const {
56    return mVersion;
57}
58
59bool ID3::parseV2(const sp<DataSource> &source) {
60    struct id3_header {
61        char id[3];
62        uint8_t version_major;
63        uint8_t version_minor;
64        uint8_t flags;
65        uint8_t enc_size[4];
66    };
67
68    id3_header header;
69    if (source->readAt(
70                0, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
71        return false;
72    }
73
74    if (memcmp(header.id, "ID3", 3)) {
75        return false;
76    }
77
78    if (header.version_major == 0xff || header.version_minor == 0xff) {
79        return false;
80    }
81
82    if (header.version_major == 2) {
83        if (header.flags & 0x3f) {
84            // We only support the 2 high bits, if any of the lower bits are
85            // set, we cannot guarantee to understand the tag format.
86            return false;
87        }
88
89        if (header.flags & 0x40) {
90            // No compression scheme has been decided yet, ignore the
91            // tag if compression is indicated.
92
93            return false;
94        }
95    } else if (header.version_major == 3) {
96        if (header.flags & 0x1f) {
97            // We only support the 3 high bits, if any of the lower bits are
98            // set, we cannot guarantee to understand the tag format.
99            return false;
100        }
101    } else {
102        return false;
103    }
104
105    size_t size = 0;
106    for (int32_t i = 0; i < 4; ++i) {
107        if (header.enc_size[i] & 0x80) {
108            return false;
109        }
110
111        size = (size << 7) | header.enc_size[i];
112    }
113
114    mData = (uint8_t *)malloc(size);
115
116    if (mData == NULL) {
117        return false;
118    }
119
120    mSize = size;
121
122    if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
123        return false;
124    }
125
126    if (header.flags & 0x80) {
127        LOGV("removing unsynchronization");
128        removeUnsynchronization();
129    }
130
131    mFirstFrameOffset = 0;
132    if (header.version_major == 3 && (header.flags & 0x40)) {
133        // Version 2.3 has an optional extended header.
134
135        if (mSize < 4) {
136            free(mData);
137            mData = NULL;
138
139            return false;
140        }
141
142        size_t extendedHeaderSize = U32_AT(&mData[0]) + 4;
143
144        if (extendedHeaderSize > mSize) {
145            free(mData);
146            mData = NULL;
147
148            return false;
149        }
150
151        mFirstFrameOffset = extendedHeaderSize;
152
153        uint16_t extendedFlags = 0;
154        if (extendedHeaderSize >= 6) {
155            extendedFlags = U16_AT(&mData[4]);
156
157            if (extendedHeaderSize >= 10) {
158                size_t paddingSize = U32_AT(&mData[6]);
159
160                if (mFirstFrameOffset + paddingSize > mSize) {
161                    free(mData);
162                    mData = NULL;
163
164                    return false;
165                }
166
167                mSize -= paddingSize;
168            }
169
170            if (extendedFlags & 0x8000) {
171                LOGV("have crc");
172            }
173        }
174    }
175
176    if (header.version_major == 2) {
177        mVersion = ID3_V2_2;
178    } else {
179        CHECK_EQ(header.version_major, 3);
180        mVersion = ID3_V2_3;
181    }
182
183    return true;
184}
185
186void ID3::removeUnsynchronization() {
187    for (size_t i = 0; i + 1 < mSize; ++i) {
188        if (mData[i] == 0xff && mData[i + 1] == 0x00) {
189            memmove(&mData[i + 1], &mData[i + 2], mSize - i - 2);
190            --mSize;
191        }
192    }
193}
194
195ID3::Iterator::Iterator(const ID3 &parent, const char *id)
196    : mParent(parent),
197      mID(NULL),
198      mOffset(mParent.mFirstFrameOffset),
199      mFrameData(NULL),
200      mFrameSize(0) {
201    if (id) {
202        mID = strdup(id);
203    }
204
205    findFrame();
206}
207
208ID3::Iterator::~Iterator() {
209    if (mID) {
210        free(mID);
211        mID = NULL;
212    }
213}
214
215bool ID3::Iterator::done() const {
216    return mFrameData == NULL;
217}
218
219void ID3::Iterator::next() {
220    if (mFrameData == NULL) {
221        return;
222    }
223
224    mOffset += mFrameSize;
225
226    findFrame();
227}
228
229void ID3::Iterator::getID(String8 *id) const {
230    id->setTo("");
231
232    if (mFrameData == NULL) {
233        return;
234    }
235
236    if (mParent.mVersion == ID3_V2_2) {
237        id->setTo((const char *)&mParent.mData[mOffset], 3);
238    } else if (mParent.mVersion == ID3_V2_3) {
239        id->setTo((const char *)&mParent.mData[mOffset], 4);
240    } else {
241        CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
242
243        switch (mOffset) {
244            case 3:
245                id->setTo("TT2");
246                break;
247            case 33:
248                id->setTo("TP1");
249                break;
250            case 63:
251                id->setTo("TAL");
252                break;
253            case 93:
254                id->setTo("TYE");
255                break;
256            case 97:
257                id->setTo("COM");
258                break;
259            case 126:
260                id->setTo("TRK");
261                break;
262            case 127:
263                id->setTo("TCO");
264                break;
265            default:
266                CHECK(!"should not be here.");
267                break;
268        }
269    }
270}
271
272static void convertISO8859ToString8(
273        const uint8_t *data, size_t size,
274        String8 *s) {
275    size_t utf8len = 0;
276    for (size_t i = 0; i < size; ++i) {
277        if (data[i] == '\0') {
278            size = i;
279            break;
280        } else if (data[i] < 0x80) {
281            ++utf8len;
282        } else {
283            utf8len += 2;
284        }
285    }
286
287    if (utf8len == size) {
288        // Only ASCII characters present.
289
290        s->setTo((const char *)data, size);
291        return;
292    }
293
294    char *tmp = new char[utf8len];
295    char *ptr = tmp;
296    for (size_t i = 0; i < size; ++i) {
297        if (data[i] == '\0') {
298            break;
299        } else if (data[i] < 0x80) {
300            *ptr++ = data[i];
301        } else if (data[i] < 0xc0) {
302            *ptr++ = 0xc2;
303            *ptr++ = data[i];
304        } else {
305            *ptr++ = 0xc3;
306            *ptr++ = data[i] - 64;
307        }
308    }
309
310    s->setTo(tmp, utf8len);
311
312    delete[] tmp;
313    tmp = NULL;
314}
315
316void ID3::Iterator::getString(String8 *id) const {
317    id->setTo("");
318
319    if (mFrameData == NULL) {
320        return;
321    }
322
323    if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) {
324        if (mOffset == 126 || mOffset == 127) {
325            // Special treatment for the track number and genre.
326            char tmp[16];
327            sprintf(tmp, "%d", (int)*mFrameData);
328
329            id->setTo(tmp);
330            return;
331        }
332
333        convertISO8859ToString8(mFrameData, mFrameSize, id);
334        return;
335    }
336
337    size_t n = mFrameSize - getHeaderLength() - 1;
338
339    if (*mFrameData == 0x00) {
340        // ISO 8859-1
341        convertISO8859ToString8(mFrameData + 1, n, id);
342    } else {
343        // UCS-2
344        // API wants number of characters, not number of bytes...
345        int len = n / 2;
346        const char16_t *framedata = (const char16_t *) (mFrameData + 1);
347        char16_t *framedatacopy = NULL;
348        if (*framedata == 0xfffe) {
349            // endianness marker doesn't match host endianness, convert
350            framedatacopy = new char16_t[len];
351            for (int i = 0; i < len; i++) {
352                framedatacopy[i] = bswap_16(framedata[i]);
353            }
354            framedata = framedatacopy;
355        }
356        // If the string starts with an endianness marker, skip it
357        if (*framedata == 0xfeff) {
358            framedata++;
359            len--;
360        }
361        id->setTo(framedata, len);
362        if (framedatacopy != NULL) {
363            delete[] framedatacopy;
364        }
365    }
366}
367
368const uint8_t *ID3::Iterator::getData(size_t *length) const {
369    *length = 0;
370
371    if (mFrameData == NULL) {
372        return NULL;
373    }
374
375    *length = mFrameSize - getHeaderLength();
376
377    return mFrameData;
378}
379
380size_t ID3::Iterator::getHeaderLength() const {
381    if (mParent.mVersion == ID3_V2_2) {
382        return 6;
383    } else if (mParent.mVersion == ID3_V2_3) {
384        return 10;
385    } else {
386        CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
387        return 0;
388    }
389}
390
391void ID3::Iterator::findFrame() {
392    for (;;) {
393        mFrameData = NULL;
394        mFrameSize = 0;
395
396        if (mParent.mVersion == ID3_V2_2) {
397            if (mOffset + 6 > mParent.mSize) {
398                return;
399            }
400
401            if (!memcmp(&mParent.mData[mOffset], "\0\0\0", 3)) {
402                return;
403            }
404
405            mFrameSize =
406                (mParent.mData[mOffset + 3] << 16)
407                | (mParent.mData[mOffset + 4] << 8)
408                | mParent.mData[mOffset + 5];
409
410            mFrameSize += 6;
411
412            if (mOffset + mFrameSize > mParent.mSize) {
413                LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
414                     mOffset, mFrameSize, mParent.mSize - mOffset - 6);
415                return;
416            }
417
418            mFrameData = &mParent.mData[mOffset + 6];
419
420            if (!mID) {
421                break;
422            }
423
424            char id[4];
425            memcpy(id, &mParent.mData[mOffset], 3);
426            id[3] = '\0';
427
428            if (!strcmp(id, mID)) {
429                break;
430            }
431        } else if (mParent.mVersion == ID3_V2_3) {
432            if (mOffset + 10 > mParent.mSize) {
433                return;
434            }
435
436            if (!memcmp(&mParent.mData[mOffset], "\0\0\0\0", 4)) {
437                return;
438            }
439
440            mFrameSize = 10 + U32_AT(&mParent.mData[mOffset + 4]);
441
442            if (mOffset + mFrameSize > mParent.mSize) {
443                LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
444                     mOffset, mFrameSize, mParent.mSize - mOffset - 10);
445                return;
446            }
447
448            mFrameData = &mParent.mData[mOffset + 10];
449
450            if (!mID) {
451                break;
452            }
453
454            char id[5];
455            memcpy(id, &mParent.mData[mOffset], 4);
456            id[4] = '\0';
457
458            if (!strcmp(id, mID)) {
459                break;
460            }
461        } else {
462            CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
463
464            if (mOffset >= mParent.mSize) {
465                return;
466            }
467
468            mFrameData = &mParent.mData[mOffset];
469
470            switch (mOffset) {
471                case 3:
472                case 33:
473                case 63:
474                    mFrameSize = 30;
475                    break;
476                case 93:
477                    mFrameSize = 4;
478                    break;
479                case 97:
480                    if (mParent.mVersion == ID3_V1) {
481                        mFrameSize = 30;
482                    } else {
483                        mFrameSize = 29;
484                    }
485                    break;
486                case 126:
487                    mFrameSize = 1;
488                    break;
489                case 127:
490                    mFrameSize = 1;
491                    break;
492                default:
493                    CHECK(!"Should not be here, invalid offset.");
494                    break;
495            }
496
497            if (!mID) {
498                break;
499            }
500
501            String8 id;
502            getID(&id);
503
504            if (id == mID) {
505                break;
506            }
507        }
508
509        mOffset += mFrameSize;
510    }
511}
512
513static size_t StringSize(const uint8_t *start, uint8_t encoding) {
514    if (encoding== 0x00) {
515        // ISO 8859-1
516        return strlen((const char *)start) + 1;
517    }
518
519    // UCS-2
520    size_t n = 0;
521    while (start[n] != '\0' || start[n + 1] != '\0') {
522        n += 2;
523    }
524
525    return n;
526}
527
528const void *
529ID3::getAlbumArt(size_t *length, String8 *mime) const {
530    *length = 0;
531    mime->setTo("");
532
533    Iterator it(*this, mVersion == ID3_V2_3 ? "APIC" : "PIC");
534
535    while (!it.done()) {
536        size_t size;
537        const uint8_t *data = it.getData(&size);
538
539        if (mVersion == ID3_V2_3) {
540            uint8_t encoding = data[0];
541            mime->setTo((const char *)&data[1]);
542            size_t mimeLen = strlen((const char *)&data[1]) + 1;
543
544            uint8_t picType = data[1 + mimeLen];
545#if 0
546            if (picType != 0x03) {
547                // Front Cover Art
548                it.next();
549                continue;
550            }
551#endif
552
553            size_t descLen = StringSize(&data[2 + mimeLen], encoding);
554
555            *length = size - 2 - mimeLen - descLen;
556
557            return &data[2 + mimeLen + descLen];
558        } else {
559            uint8_t encoding = data[0];
560
561            if (!memcmp(&data[1], "PNG", 3)) {
562                mime->setTo("image/png");
563            } else if (!memcmp(&data[1], "JPG", 3)) {
564                mime->setTo("image/jpeg");
565            } else if (!memcmp(&data[1], "-->", 3)) {
566                mime->setTo("text/plain");
567            } else {
568                return NULL;
569            }
570
571#if 0
572            uint8_t picType = data[4];
573            if (picType != 0x03) {
574                // Front Cover Art
575                it.next();
576                continue;
577            }
578#endif
579
580            size_t descLen = StringSize(&data[5], encoding);
581
582            *length = size - 5 - descLen;
583
584            return &data[5 + descLen];
585        }
586    }
587
588    return NULL;
589}
590
591bool ID3::parseV1(const sp<DataSource> &source) {
592    const size_t V1_TAG_SIZE = 128;
593
594    off_t size;
595    if (source->getSize(&size) != OK || size < (off_t)V1_TAG_SIZE) {
596        return false;
597    }
598
599    mData = (uint8_t *)malloc(V1_TAG_SIZE);
600    if (source->readAt(size - V1_TAG_SIZE, mData, V1_TAG_SIZE)
601            != (ssize_t)V1_TAG_SIZE) {
602        free(mData);
603        mData = NULL;
604
605        return false;
606    }
607
608    if (memcmp("TAG", mData, 3)) {
609        free(mData);
610        mData = NULL;
611
612        return false;
613    }
614
615    mSize = V1_TAG_SIZE;
616    mFirstFrameOffset = 3;
617
618    if (mData[V1_TAG_SIZE - 3] != 0) {
619        mVersion = ID3_V1;
620    } else {
621        mVersion = ID3_V1_1;
622    }
623
624    return true;
625}
626
627}  // namespace android
628