ID3.cpp revision fc9ba09e3bb368f823d473f5e2bb9aa32dba6289
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
28namespace android {
29
30ID3::ID3(const sp<DataSource> &source)
31    : mIsValid(false),
32      mData(NULL),
33      mSize(0),
34      mFirstFrameOffset(0),
35      mVersion(ID3_UNKNOWN) {
36    mIsValid = parse(source);
37}
38
39ID3::~ID3() {
40    if (mData) {
41        free(mData);
42        mData = NULL;
43    }
44}
45
46bool ID3::isValid() const {
47    return mIsValid;
48}
49
50ID3::Version ID3::version() const {
51    return mVersion;
52}
53
54bool ID3::parse(const sp<DataSource> &source) {
55    struct id3_header {
56        char id[3];
57        uint8_t version_major;
58        uint8_t version_minor;
59        uint8_t flags;
60        uint8_t enc_size[4];
61    };
62
63    id3_header header;
64    if (source->readAt(
65                0, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
66        return false;
67    }
68
69    if (memcmp(header.id, "ID3", 3)) {
70        return false;
71    }
72
73    if (header.version_major == 0xff || header.version_minor == 0xff) {
74        return false;
75    }
76
77    if (header.version_major == 2) {
78        if (header.flags & 0x3f) {
79            // We only support the 2 high bits, if any of the lower bits are
80            // set, we cannot guarantee to understand the tag format.
81            return false;
82        }
83
84        if (header.flags & 0x40) {
85            // No compression scheme has been decided yet, ignore the
86            // tag if compression is indicated.
87
88            return false;
89        }
90    } else if (header.version_major == 3) {
91        if (header.flags & 0x1f) {
92            // We only support the 3 high bits, if any of the lower bits are
93            // set, we cannot guarantee to understand the tag format.
94            return false;
95        }
96    } else {
97        return false;
98    }
99
100    size_t size = 0;
101    for (int32_t i = 0; i < 4; ++i) {
102        if (header.enc_size[i] & 0x80) {
103            return false;
104        }
105
106        size = (size << 7) | header.enc_size[i];
107    }
108
109    mData = (uint8_t *)malloc(size);
110
111    if (mData == NULL) {
112        return false;
113    }
114
115    mSize = size;
116
117    if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
118        return false;
119    }
120
121    if (header.flags & 0x80) {
122        LOGI("removing unsynchronization");
123        removeUnsynchronization();
124    }
125
126    mFirstFrameOffset = 0;
127    if (header.version_major == 3 && (header.flags & 0x40)) {
128        // Version 2.3 has an optional extended header.
129
130        if (mSize < 4) {
131            return false;
132        }
133
134        size_t extendedHeaderSize = U32_AT(&mData[0]) + 4;
135
136        if (extendedHeaderSize > mSize) {
137            return false;
138        }
139
140        mFirstFrameOffset = extendedHeaderSize;
141
142        uint16_t extendedFlags = 0;
143        if (extendedHeaderSize >= 6) {
144            extendedFlags = U16_AT(&mData[4]);
145
146            if (extendedHeaderSize >= 10) {
147                size_t paddingSize = U32_AT(&mData[6]);
148
149                if (mFirstFrameOffset + paddingSize > mSize) {
150                    return false;
151                }
152
153                mSize -= paddingSize;
154            }
155
156            if (extendedFlags & 0x8000) {
157                LOGI("have crc");
158            }
159        }
160    }
161
162    if (header.version_major == 2) {
163        mVersion = ID3_V2_2;
164    } else {
165        CHECK_EQ(header.version_major, 3);
166        mVersion = ID3_V2_3;
167    }
168
169    return true;
170}
171
172void ID3::removeUnsynchronization() {
173    for (size_t i = 0; i + 1 < mSize; ++i) {
174        if (mData[i] == 0xff && mData[i + 1] == 0x00) {
175            memmove(&mData[i + 1], &mData[i + 2], mSize - i - 2);
176            --mSize;
177        }
178    }
179}
180
181ID3::Iterator::Iterator(const ID3 &parent, const char *id)
182    : mParent(parent),
183      mID(NULL),
184      mOffset(mParent.mFirstFrameOffset),
185      mFrameData(NULL),
186      mFrameSize(0) {
187    if (id) {
188        mID = strdup(id);
189    }
190
191    findFrame();
192}
193
194ID3::Iterator::~Iterator() {
195    if (mID) {
196        free(mID);
197        mID = NULL;
198    }
199}
200
201bool ID3::Iterator::done() const {
202    return mFrameData == NULL;
203}
204
205void ID3::Iterator::next() {
206    if (mFrameData == NULL) {
207        return;
208    }
209
210    mOffset += mFrameSize;
211
212    findFrame();
213}
214
215void ID3::Iterator::getID(String8 *id) const {
216    id->setTo("");
217
218    if (mFrameData == NULL) {
219        return;
220    }
221
222    if (mParent.mVersion == ID3_V2_2) {
223        id->setTo((const char *)&mParent.mData[mOffset], 3);
224    } else {
225        CHECK_EQ(mParent.mVersion, ID3_V2_3);
226        id->setTo((const char *)&mParent.mData[mOffset], 4);
227    }
228}
229
230static void convertISO8859ToString8(
231        const uint8_t *data, size_t size,
232        String8 *s) {
233    size_t utf8len = 0;
234    for (size_t i = 0; i < size; ++i) {
235        if (data[i] < 0x80) {
236            ++utf8len;
237        } else {
238            utf8len += 2;
239        }
240    }
241
242    if (utf8len == size) {
243        // Only ASCII characters present.
244
245        s->setTo((const char *)data, size);
246        return;
247    }
248
249    char *tmp = new char[utf8len];
250    char *ptr = tmp;
251    for (size_t i = 0; i < size; ++i) {
252        if (data[i] < 0x80) {
253            *ptr++ = data[i];
254        } else if (data[i] < 0xc0) {
255            *ptr++ = 0xc2;
256            *ptr++ = data[i];
257        } else {
258            *ptr++ = 0xc3;
259            *ptr++ = data[i] - 64;
260        }
261    }
262
263    s->setTo(tmp, utf8len);
264
265    delete[] tmp;
266    tmp = NULL;
267}
268
269void ID3::Iterator::getString(String8 *id) const {
270    id->setTo("");
271
272    if (mFrameData == NULL) {
273        return;
274    }
275
276    size_t n = mFrameSize - getHeaderLength() - 1;
277
278    if (*mFrameData == 0x00) {
279        // ISO 8859-1
280        convertISO8859ToString8(mFrameData + 1, n, id);
281    } else {
282        // UCS-2
283        id->setTo((const char16_t *)(mFrameData + 1), n);
284    }
285}
286
287const uint8_t *ID3::Iterator::getData(size_t *length) const {
288    *length = 0;
289
290    if (mFrameData == NULL) {
291        return NULL;
292    }
293
294    *length = mFrameSize - getHeaderLength();
295
296    return mFrameData;
297}
298
299size_t ID3::Iterator::getHeaderLength() const {
300    if (mParent.mVersion == ID3_V2_2) {
301        return 6;
302    } else {
303        CHECK_EQ(mParent.mVersion, ID3_V2_3);
304        return 10;
305    }
306}
307
308void ID3::Iterator::findFrame() {
309    for (;;) {
310        mFrameData = NULL;
311        mFrameSize = 0;
312
313        if (mParent.mVersion == ID3_V2_2) {
314            if (mOffset + 6 > mParent.mSize) {
315                return;
316            }
317
318            if (!memcmp(&mParent.mData[mOffset], "\0\0\0", 3)) {
319                return;
320            }
321
322            mFrameSize =
323                (mParent.mData[mOffset + 3] << 16)
324                | (mParent.mData[mOffset + 4] << 8)
325                | mParent.mData[mOffset + 5];
326
327            mFrameSize += 6;
328
329            if (mOffset + mFrameSize > mParent.mSize) {
330                LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
331                     mOffset, mFrameSize, mParent.mSize - mOffset - 6);
332                return;
333            }
334
335            mFrameData = &mParent.mData[mOffset + 6];
336
337            if (!mID) {
338                break;
339            }
340
341            char id[4];
342            memcpy(id, &mParent.mData[mOffset], 3);
343            id[3] = '\0';
344
345            if (!strcmp(id, mID)) {
346                break;
347            }
348        } else {
349            CHECK_EQ(mParent.mVersion, ID3_V2_3);
350
351            if (mOffset + 10 > mParent.mSize) {
352                return;
353            }
354
355            if (!memcmp(&mParent.mData[mOffset], "\0\0\0\0", 4)) {
356                return;
357            }
358
359            mFrameSize = 10 + U32_AT(&mParent.mData[mOffset + 4]);
360
361            if (mOffset + mFrameSize > mParent.mSize) {
362                LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
363                     mOffset, mFrameSize, mParent.mSize - mOffset - 10);
364                return;
365            }
366
367            mFrameData = &mParent.mData[mOffset + 10];
368
369            if (!mID) {
370                break;
371            }
372
373            char id[5];
374            memcpy(id, &mParent.mData[mOffset], 4);
375            id[4] = '\0';
376
377            if (!strcmp(id, mID)) {
378                break;
379            }
380        }
381
382        mOffset += mFrameSize;
383    }
384}
385
386static size_t StringSize(const uint8_t *start, uint8_t encoding) {
387    if (encoding== 0x00) {
388        // ISO 8859-1
389        return strlen((const char *)start) + 1;
390    }
391
392    // UCS-2
393    size_t n = 0;
394    while (start[n] != '\0' || start[n + 1] != '\0') {
395        n += 2;
396    }
397
398    return n;
399}
400
401const void *
402ID3::getAlbumArt(size_t *length, String8 *mime) const {
403    *length = 0;
404    mime->setTo("");
405
406    Iterator it(*this, mVersion == ID3_V2_3 ? "APIC" : "PIC");
407
408    while (!it.done()) {
409        size_t size;
410        const uint8_t *data = it.getData(&size);
411
412        if (mVersion == ID3_V2_3) {
413            uint8_t encoding = data[0];
414            mime->setTo((const char *)&data[1]);
415            size_t mimeLen = strlen((const char *)&data[1]) + 1;
416
417            uint8_t picType = data[1 + mimeLen];
418#if 0
419            if (picType != 0x03) {
420                // Front Cover Art
421                it.next();
422                continue;
423            }
424#endif
425
426            size_t descLen = StringSize(&data[2 + mimeLen], encoding);
427
428            *length = size - 2 - mimeLen - descLen;
429
430            return &data[2 + mimeLen + descLen];
431        } else {
432            uint8_t encoding = data[0];
433
434            if (!memcmp(&data[1], "PNG", 3)) {
435                mime->setTo("image/png");
436            } else if (!memcmp(&data[1], "JPG", 3)) {
437                mime->setTo("image/jpeg");
438            } else if (!memcmp(&data[1], "-->", 3)) {
439                mime->setTo("text/plain");
440            } else {
441                return NULL;
442            }
443
444#if 0
445            uint8_t picType = data[4];
446            if (picType != 0x03) {
447                // Front Cover Art
448                it.next();
449                continue;
450            }
451#endif
452
453            size_t descLen = StringSize(&data[5], encoding);
454
455            *length = size - 5 - descLen;
456
457            return &data[5 + descLen];
458        }
459    }
460
461    return NULL;
462}
463
464
465}  // namespace android
466