blob: 45e018d8c3ccd7eb2ed7bfde8a0cb191ed98dd8a [file] [log] [blame]
Andreas Huberaee3c632010-01-11 15:35:19 -08001/*
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>
Marco Nelissene56b7e6c2010-02-09 07:40:10 -080027#include <byteswap.h>
Andreas Huberaee3c632010-01-11 15:35:19 -080028
29namespace android {
30
Andreas Huberc2eeb2f2010-03-29 15:13:40 -070031static const size_t kMaxMetadataSize = 3 * 1024 * 1024;
32
Andreas Huberaee3c632010-01-11 15:35:19 -080033ID3::ID3(const sp<DataSource> &source)
34 : mIsValid(false),
35 mData(NULL),
36 mSize(0),
37 mFirstFrameOffset(0),
38 mVersion(ID3_UNKNOWN) {
Andreas Huber43782d32010-01-19 13:35:27 -080039 mIsValid = parseV2(source);
40
41 if (!mIsValid) {
42 mIsValid = parseV1(source);
43 }
Andreas Huberaee3c632010-01-11 15:35:19 -080044}
45
46ID3::~ID3() {
47 if (mData) {
48 free(mData);
49 mData = NULL;
50 }
51}
52
53bool ID3::isValid() const {
54 return mIsValid;
55}
56
57ID3::Version ID3::version() const {
58 return mVersion;
59}
60
Andreas Huberc944cbe2010-04-08 14:01:33 -070061// static
62bool ID3::ParseSyncsafeInteger(const uint8_t encoded[4], size_t *x) {
63 *x = 0;
64 for (int32_t i = 0; i < 4; ++i) {
65 if (encoded[i] & 0x80) {
66 return false;
67 }
68
69 *x = ((*x) << 7) | encoded[i];
70 }
71
72 return true;
73}
74
Andreas Huber43782d32010-01-19 13:35:27 -080075bool ID3::parseV2(const sp<DataSource> &source) {
Andreas Huberc944cbe2010-04-08 14:01:33 -070076struct id3_header {
77 char id[3];
78 uint8_t version_major;
79 uint8_t version_minor;
80 uint8_t flags;
81 uint8_t enc_size[4];
Andreas Huberaee3c632010-01-11 15:35:19 -080082 };
83
84 id3_header header;
85 if (source->readAt(
86 0, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
87 return false;
88 }
89
90 if (memcmp(header.id, "ID3", 3)) {
91 return false;
92 }
93
94 if (header.version_major == 0xff || header.version_minor == 0xff) {
95 return false;
96 }
97
98 if (header.version_major == 2) {
99 if (header.flags & 0x3f) {
100 // We only support the 2 high bits, if any of the lower bits are
101 // set, we cannot guarantee to understand the tag format.
102 return false;
103 }
104
105 if (header.flags & 0x40) {
106 // No compression scheme has been decided yet, ignore the
107 // tag if compression is indicated.
108
109 return false;
110 }
111 } else if (header.version_major == 3) {
112 if (header.flags & 0x1f) {
113 // We only support the 3 high bits, if any of the lower bits are
114 // set, we cannot guarantee to understand the tag format.
115 return false;
116 }
Andreas Huberc944cbe2010-04-08 14:01:33 -0700117 } else if (header.version_major == 4) {
118 if (header.flags & 0x0f) {
119 // The lower 4 bits are undefined in this spec.
120 return false;
121 }
Andreas Huberaee3c632010-01-11 15:35:19 -0800122 } else {
123 return false;
124 }
125
Andreas Huberc944cbe2010-04-08 14:01:33 -0700126 size_t size;
127 if (!ParseSyncsafeInteger(header.enc_size, &size)) {
128 return false;
Andreas Huberaee3c632010-01-11 15:35:19 -0800129 }
130
Andreas Huberc2eeb2f2010-03-29 15:13:40 -0700131 if (size > kMaxMetadataSize) {
132 LOGE("skipping huge ID3 metadata of size %d", size);
133 return false;
134 }
135
Andreas Huberaee3c632010-01-11 15:35:19 -0800136 mData = (uint8_t *)malloc(size);
137
138 if (mData == NULL) {
139 return false;
140 }
141
142 mSize = size;
143
144 if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
Andreas Huber8735f892010-08-25 14:55:53 -0700145 free(mData);
146 mData = NULL;
147
Andreas Huberaee3c632010-01-11 15:35:19 -0800148 return false;
149 }
150
Andreas Huber8735f892010-08-25 14:55:53 -0700151 if (header.version_major == 4) {
Andreas Huber5d266dd2010-12-14 09:49:29 -0800152 void *copy = malloc(size);
153 memcpy(copy, mData, size);
154
155 bool success = removeUnsynchronizationV2_4(false /* iTunesHack */);
156 if (!success) {
157 memcpy(mData, copy, size);
158 mSize = size;
159
160 success = removeUnsynchronizationV2_4(true /* iTunesHack */);
161
162 if (success) {
163 LOGV("Had to apply the iTunes hack to parse this ID3 tag");
164 }
165 }
166
167 free(copy);
168 copy = NULL;
169
170 if (!success) {
Andreas Huber8735f892010-08-25 14:55:53 -0700171 free(mData);
172 mData = NULL;
173
174 return false;
175 }
176 } else if (header.flags & 0x80) {
Andreas Huber43782d32010-01-19 13:35:27 -0800177 LOGV("removing unsynchronization");
Andreas Huber8735f892010-08-25 14:55:53 -0700178
Andreas Huberaee3c632010-01-11 15:35:19 -0800179 removeUnsynchronization();
180 }
181
182 mFirstFrameOffset = 0;
183 if (header.version_major == 3 && (header.flags & 0x40)) {
184 // Version 2.3 has an optional extended header.
185
186 if (mSize < 4) {
Andreas Huber43782d32010-01-19 13:35:27 -0800187 free(mData);
188 mData = NULL;
189
Andreas Huberaee3c632010-01-11 15:35:19 -0800190 return false;
191 }
192
193 size_t extendedHeaderSize = U32_AT(&mData[0]) + 4;
194
195 if (extendedHeaderSize > mSize) {
Andreas Huber43782d32010-01-19 13:35:27 -0800196 free(mData);
197 mData = NULL;
198
Andreas Huberaee3c632010-01-11 15:35:19 -0800199 return false;
200 }
201
202 mFirstFrameOffset = extendedHeaderSize;
203
204 uint16_t extendedFlags = 0;
205 if (extendedHeaderSize >= 6) {
206 extendedFlags = U16_AT(&mData[4]);
207
208 if (extendedHeaderSize >= 10) {
209 size_t paddingSize = U32_AT(&mData[6]);
210
211 if (mFirstFrameOffset + paddingSize > mSize) {
Andreas Huber43782d32010-01-19 13:35:27 -0800212 free(mData);
213 mData = NULL;
214
Andreas Huberaee3c632010-01-11 15:35:19 -0800215 return false;
216 }
217
218 mSize -= paddingSize;
219 }
220
221 if (extendedFlags & 0x8000) {
Andreas Huber43782d32010-01-19 13:35:27 -0800222 LOGV("have crc");
Andreas Huberaee3c632010-01-11 15:35:19 -0800223 }
224 }
Andreas Huberc944cbe2010-04-08 14:01:33 -0700225 } else if (header.version_major == 4 && (header.flags & 0x40)) {
226 // Version 2.4 has an optional extended header, that's different
227 // from Version 2.3's...
228
229 if (mSize < 4) {
230 free(mData);
231 mData = NULL;
232
233 return false;
234 }
235
236 size_t ext_size;
237 if (!ParseSyncsafeInteger(mData, &ext_size)) {
238 free(mData);
239 mData = NULL;
240
241 return false;
242 }
243
244 if (ext_size < 6 || ext_size > mSize) {
245 free(mData);
246 mData = NULL;
247
248 return false;
249 }
250
251 mFirstFrameOffset = ext_size;
Andreas Huberaee3c632010-01-11 15:35:19 -0800252 }
253
254 if (header.version_major == 2) {
255 mVersion = ID3_V2_2;
Andreas Huberc944cbe2010-04-08 14:01:33 -0700256 } else if (header.version_major == 3) {
Andreas Huberaee3c632010-01-11 15:35:19 -0800257 mVersion = ID3_V2_3;
Andreas Huberc944cbe2010-04-08 14:01:33 -0700258 } else {
259 CHECK_EQ(header.version_major, 4);
260 mVersion = ID3_V2_4;
Andreas Huberaee3c632010-01-11 15:35:19 -0800261 }
262
263 return true;
264}
265
266void ID3::removeUnsynchronization() {
267 for (size_t i = 0; i + 1 < mSize; ++i) {
268 if (mData[i] == 0xff && mData[i + 1] == 0x00) {
269 memmove(&mData[i + 1], &mData[i + 2], mSize - i - 2);
270 --mSize;
271 }
272 }
273}
274
Andreas Huber8735f892010-08-25 14:55:53 -0700275static void WriteSyncsafeInteger(uint8_t *dst, size_t x) {
276 for (size_t i = 0; i < 4; ++i) {
277 dst[3 - i] = (x & 0x7f);
278 x >>= 7;
279 }
280}
281
Andreas Huber5d266dd2010-12-14 09:49:29 -0800282bool ID3::removeUnsynchronizationV2_4(bool iTunesHack) {
Andreas Huber8735f892010-08-25 14:55:53 -0700283 size_t oldSize = mSize;
284
285 size_t offset = 0;
286 while (offset + 10 <= mSize) {
287 if (!memcmp(&mData[offset], "\0\0\0\0", 4)) {
288 break;
289 }
290
291 size_t dataSize;
Andreas Huber5d266dd2010-12-14 09:49:29 -0800292 if (iTunesHack) {
293 dataSize = U32_AT(&mData[offset + 4]);
294 } else if (!ParseSyncsafeInteger(&mData[offset + 4], &dataSize)) {
Andreas Huber8735f892010-08-25 14:55:53 -0700295 return false;
296 }
297
298 if (offset + dataSize + 10 > mSize) {
299 return false;
300 }
301
302 uint16_t flags = U16_AT(&mData[offset + 8]);
303 uint16_t prevFlags = flags;
304
305 if (flags & 1) {
306 // Strip data length indicator
307
308 memmove(&mData[offset + 10], &mData[offset + 14], mSize - offset - 14);
309 mSize -= 4;
310 dataSize -= 4;
311
312 flags &= ~1;
313 }
314
315 if (flags & 2) {
316 // Unsynchronization added.
317
318 for (size_t i = 0; i + 1 < dataSize; ++i) {
319 if (mData[offset + 10 + i] == 0xff
320 && mData[offset + 11 + i] == 0x00) {
321 memmove(&mData[offset + 11 + i], &mData[offset + 12 + i],
322 mSize - offset - 12 - i);
323 --mSize;
324 --dataSize;
325 }
326 }
327
328 flags &= ~2;
329 }
330
Andreas Huber5d266dd2010-12-14 09:49:29 -0800331 if (flags != prevFlags || iTunesHack) {
Andreas Huber8735f892010-08-25 14:55:53 -0700332 WriteSyncsafeInteger(&mData[offset + 4], dataSize);
333 mData[offset + 8] = flags >> 8;
334 mData[offset + 9] = flags & 0xff;
335 }
336
337 offset += 10 + dataSize;
338 }
339
340 memset(&mData[mSize], 0, oldSize - mSize);
341
342 return true;
343}
344
Andreas Huberaee3c632010-01-11 15:35:19 -0800345ID3::Iterator::Iterator(const ID3 &parent, const char *id)
346 : mParent(parent),
347 mID(NULL),
348 mOffset(mParent.mFirstFrameOffset),
349 mFrameData(NULL),
350 mFrameSize(0) {
351 if (id) {
352 mID = strdup(id);
353 }
354
355 findFrame();
356}
357
358ID3::Iterator::~Iterator() {
359 if (mID) {
360 free(mID);
361 mID = NULL;
362 }
363}
364
365bool ID3::Iterator::done() const {
366 return mFrameData == NULL;
367}
368
369void ID3::Iterator::next() {
370 if (mFrameData == NULL) {
371 return;
372 }
373
374 mOffset += mFrameSize;
375
376 findFrame();
377}
378
379void ID3::Iterator::getID(String8 *id) const {
380 id->setTo("");
381
382 if (mFrameData == NULL) {
383 return;
384 }
385
386 if (mParent.mVersion == ID3_V2_2) {
387 id->setTo((const char *)&mParent.mData[mOffset], 3);
Andreas Huberc944cbe2010-04-08 14:01:33 -0700388 } else if (mParent.mVersion == ID3_V2_3 || mParent.mVersion == ID3_V2_4) {
Andreas Huberaee3c632010-01-11 15:35:19 -0800389 id->setTo((const char *)&mParent.mData[mOffset], 4);
Andreas Huber43782d32010-01-19 13:35:27 -0800390 } else {
391 CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
392
393 switch (mOffset) {
394 case 3:
395 id->setTo("TT2");
396 break;
397 case 33:
398 id->setTo("TP1");
399 break;
400 case 63:
401 id->setTo("TAL");
402 break;
403 case 93:
404 id->setTo("TYE");
405 break;
406 case 97:
407 id->setTo("COM");
408 break;
409 case 126:
410 id->setTo("TRK");
411 break;
412 case 127:
413 id->setTo("TCO");
414 break;
415 default:
416 CHECK(!"should not be here.");
417 break;
418 }
Andreas Huberaee3c632010-01-11 15:35:19 -0800419 }
420}
421
422static void convertISO8859ToString8(
423 const uint8_t *data, size_t size,
424 String8 *s) {
425 size_t utf8len = 0;
426 for (size_t i = 0; i < size; ++i) {
Andreas Huber9be54d42010-02-08 11:04:56 -0800427 if (data[i] == '\0') {
Kenny Root0c2ab242010-03-15 22:45:02 -0700428 size = i;
Andreas Huber9be54d42010-02-08 11:04:56 -0800429 break;
430 } else if (data[i] < 0x80) {
Andreas Huberaee3c632010-01-11 15:35:19 -0800431 ++utf8len;
432 } else {
433 utf8len += 2;
434 }
435 }
436
437 if (utf8len == size) {
438 // Only ASCII characters present.
439
440 s->setTo((const char *)data, size);
441 return;
442 }
443
444 char *tmp = new char[utf8len];
445 char *ptr = tmp;
446 for (size_t i = 0; i < size; ++i) {
Andreas Huber9be54d42010-02-08 11:04:56 -0800447 if (data[i] == '\0') {
448 break;
449 } else if (data[i] < 0x80) {
Andreas Huberaee3c632010-01-11 15:35:19 -0800450 *ptr++ = data[i];
451 } else if (data[i] < 0xc0) {
452 *ptr++ = 0xc2;
453 *ptr++ = data[i];
454 } else {
455 *ptr++ = 0xc3;
456 *ptr++ = data[i] - 64;
457 }
458 }
459
460 s->setTo(tmp, utf8len);
461
462 delete[] tmp;
463 tmp = NULL;
464}
465
466void ID3::Iterator::getString(String8 *id) const {
467 id->setTo("");
468
469 if (mFrameData == NULL) {
470 return;
471 }
472
Andreas Huber43782d32010-01-19 13:35:27 -0800473 if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) {
474 if (mOffset == 126 || mOffset == 127) {
475 // Special treatment for the track number and genre.
476 char tmp[16];
477 sprintf(tmp, "%d", (int)*mFrameData);
478
479 id->setTo(tmp);
480 return;
481 }
482
Andreas Huber9be54d42010-02-08 11:04:56 -0800483 convertISO8859ToString8(mFrameData, mFrameSize, id);
Andreas Huber43782d32010-01-19 13:35:27 -0800484 return;
485 }
486
Andreas Huberaee3c632010-01-11 15:35:19 -0800487 size_t n = mFrameSize - getHeaderLength() - 1;
488
489 if (*mFrameData == 0x00) {
490 // ISO 8859-1
491 convertISO8859ToString8(mFrameData + 1, n, id);
Andreas Huberc944cbe2010-04-08 14:01:33 -0700492 } else if (*mFrameData == 0x03) {
493 // UTF-8
494 id->setTo((const char *)(mFrameData + 1), n);
495 } else if (*mFrameData == 0x02) {
496 // UTF-16 BE, no byte order mark.
497 // API wants number of characters, not number of bytes...
498 int len = n / 2;
499 const char16_t *framedata = (const char16_t *) (mFrameData + 1);
500 char16_t *framedatacopy = NULL;
501#if BYTE_ORDER == LITTLE_ENDIAN
502 framedatacopy = new char16_t[len];
503 for (int i = 0; i < len; i++) {
504 framedatacopy[i] = bswap_16(framedata[i]);
505 }
506 framedata = framedatacopy;
507#endif
508 id->setTo(framedata, len);
509 if (framedatacopy != NULL) {
510 delete[] framedatacopy;
511 }
Andreas Huberaee3c632010-01-11 15:35:19 -0800512 } else {
513 // UCS-2
Andreas Huber5b5ae132010-01-19 13:52:06 -0800514 // API wants number of characters, not number of bytes...
Marco Nelissen3887ac72010-02-08 15:04:23 -0800515 int len = n / 2;
516 const char16_t *framedata = (const char16_t *) (mFrameData + 1);
517 char16_t *framedatacopy = NULL;
518 if (*framedata == 0xfffe) {
519 // endianness marker doesn't match host endianness, convert
520 framedatacopy = new char16_t[len];
521 for (int i = 0; i < len; i++) {
Marco Nelissene56b7e6c2010-02-09 07:40:10 -0800522 framedatacopy[i] = bswap_16(framedata[i]);
Marco Nelissen3887ac72010-02-08 15:04:23 -0800523 }
524 framedata = framedatacopy;
525 }
526 // If the string starts with an endianness marker, skip it
527 if (*framedata == 0xfeff) {
528 framedata++;
529 len--;
530 }
531 id->setTo(framedata, len);
532 if (framedatacopy != NULL) {
533 delete[] framedatacopy;
534 }
Andreas Huberaee3c632010-01-11 15:35:19 -0800535 }
536}
537
538const uint8_t *ID3::Iterator::getData(size_t *length) const {
539 *length = 0;
540
541 if (mFrameData == NULL) {
542 return NULL;
543 }
544
545 *length = mFrameSize - getHeaderLength();
546
547 return mFrameData;
548}
549
550size_t ID3::Iterator::getHeaderLength() const {
551 if (mParent.mVersion == ID3_V2_2) {
552 return 6;
Andreas Huberc944cbe2010-04-08 14:01:33 -0700553 } else if (mParent.mVersion == ID3_V2_3 || mParent.mVersion == ID3_V2_4) {
Andreas Huberaee3c632010-01-11 15:35:19 -0800554 return 10;
Andreas Huber43782d32010-01-19 13:35:27 -0800555 } else {
556 CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
557 return 0;
Andreas Huberaee3c632010-01-11 15:35:19 -0800558 }
559}
560
561void ID3::Iterator::findFrame() {
562 for (;;) {
563 mFrameData = NULL;
564 mFrameSize = 0;
565
566 if (mParent.mVersion == ID3_V2_2) {
567 if (mOffset + 6 > mParent.mSize) {
568 return;
569 }
570
571 if (!memcmp(&mParent.mData[mOffset], "\0\0\0", 3)) {
572 return;
573 }
574
575 mFrameSize =
576 (mParent.mData[mOffset + 3] << 16)
577 | (mParent.mData[mOffset + 4] << 8)
578 | mParent.mData[mOffset + 5];
579
580 mFrameSize += 6;
581
582 if (mOffset + mFrameSize > mParent.mSize) {
583 LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
584 mOffset, mFrameSize, mParent.mSize - mOffset - 6);
585 return;
586 }
587
588 mFrameData = &mParent.mData[mOffset + 6];
589
590 if (!mID) {
591 break;
592 }
593
594 char id[4];
595 memcpy(id, &mParent.mData[mOffset], 3);
596 id[3] = '\0';
597
598 if (!strcmp(id, mID)) {
599 break;
600 }
Andreas Huberc944cbe2010-04-08 14:01:33 -0700601 } else if (mParent.mVersion == ID3_V2_3
602 || mParent.mVersion == ID3_V2_4) {
Andreas Huberaee3c632010-01-11 15:35:19 -0800603 if (mOffset + 10 > mParent.mSize) {
604 return;
605 }
606
607 if (!memcmp(&mParent.mData[mOffset], "\0\0\0\0", 4)) {
608 return;
609 }
610
Andreas Huberc944cbe2010-04-08 14:01:33 -0700611 size_t baseSize;
612 if (mParent.mVersion == ID3_V2_4) {
613 if (!ParseSyncsafeInteger(
614 &mParent.mData[mOffset + 4], &baseSize)) {
615 return;
616 }
617 } else {
618 baseSize = U32_AT(&mParent.mData[mOffset + 4]);
619 }
620
621 mFrameSize = 10 + baseSize;
Andreas Huberaee3c632010-01-11 15:35:19 -0800622
623 if (mOffset + mFrameSize > mParent.mSize) {
624 LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
625 mOffset, mFrameSize, mParent.mSize - mOffset - 10);
626 return;
627 }
628
Andreas Huberc944cbe2010-04-08 14:01:33 -0700629 uint16_t flags = U16_AT(&mParent.mData[mOffset + 8]);
630
Andreas Huber8735f892010-08-25 14:55:53 -0700631 if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000c))
Andreas Huberc944cbe2010-04-08 14:01:33 -0700632 || (mParent.mVersion == ID3_V2_3 && (flags & 0x00c0))) {
Andreas Huber8735f892010-08-25 14:55:53 -0700633 // Compression or encryption are not supported at this time.
634 // Per-frame unsynchronization and data-length indicator
635 // have already been taken care of.
Andreas Huberc944cbe2010-04-08 14:01:33 -0700636
637 LOGV("Skipping unsupported frame (compression, encryption "
638 "or per-frame unsynchronization flagged");
639
640 mOffset += mFrameSize;
641 continue;
642 }
643
Andreas Huberaee3c632010-01-11 15:35:19 -0800644 mFrameData = &mParent.mData[mOffset + 10];
645
646 if (!mID) {
647 break;
648 }
649
650 char id[5];
651 memcpy(id, &mParent.mData[mOffset], 4);
652 id[4] = '\0';
653
654 if (!strcmp(id, mID)) {
655 break;
656 }
Andreas Huber43782d32010-01-19 13:35:27 -0800657 } else {
658 CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
659
660 if (mOffset >= mParent.mSize) {
661 return;
662 }
663
664 mFrameData = &mParent.mData[mOffset];
665
666 switch (mOffset) {
667 case 3:
668 case 33:
669 case 63:
670 mFrameSize = 30;
671 break;
672 case 93:
673 mFrameSize = 4;
674 break;
675 case 97:
676 if (mParent.mVersion == ID3_V1) {
677 mFrameSize = 30;
678 } else {
679 mFrameSize = 29;
680 }
681 break;
682 case 126:
683 mFrameSize = 1;
684 break;
685 case 127:
686 mFrameSize = 1;
687 break;
688 default:
689 CHECK(!"Should not be here, invalid offset.");
690 break;
691 }
692
693 if (!mID) {
694 break;
695 }
696
697 String8 id;
698 getID(&id);
699
700 if (id == mID) {
701 break;
702 }
Andreas Huberaee3c632010-01-11 15:35:19 -0800703 }
704
705 mOffset += mFrameSize;
706 }
707}
708
709static size_t StringSize(const uint8_t *start, uint8_t encoding) {
Andreas Huberc944cbe2010-04-08 14:01:33 -0700710 if (encoding == 0x00 || encoding == 0x03) {
711 // ISO 8859-1 or UTF-8
Andreas Huberaee3c632010-01-11 15:35:19 -0800712 return strlen((const char *)start) + 1;
713 }
714
715 // UCS-2
716 size_t n = 0;
717 while (start[n] != '\0' || start[n + 1] != '\0') {
718 n += 2;
719 }
720
721 return n;
722}
723
724const void *
725ID3::getAlbumArt(size_t *length, String8 *mime) const {
726 *length = 0;
727 mime->setTo("");
728
Andreas Huberc944cbe2010-04-08 14:01:33 -0700729 Iterator it(
730 *this,
731 (mVersion == ID3_V2_3 || mVersion == ID3_V2_4) ? "APIC" : "PIC");
Andreas Huberaee3c632010-01-11 15:35:19 -0800732
733 while (!it.done()) {
734 size_t size;
735 const uint8_t *data = it.getData(&size);
736
Andreas Huberc944cbe2010-04-08 14:01:33 -0700737 if (mVersion == ID3_V2_3 || mVersion == ID3_V2_4) {
Andreas Huberaee3c632010-01-11 15:35:19 -0800738 uint8_t encoding = data[0];
739 mime->setTo((const char *)&data[1]);
740 size_t mimeLen = strlen((const char *)&data[1]) + 1;
741
742 uint8_t picType = data[1 + mimeLen];
743#if 0
744 if (picType != 0x03) {
745 // Front Cover Art
746 it.next();
747 continue;
748 }
749#endif
750
751 size_t descLen = StringSize(&data[2 + mimeLen], encoding);
752
753 *length = size - 2 - mimeLen - descLen;
754
755 return &data[2 + mimeLen + descLen];
756 } else {
757 uint8_t encoding = data[0];
758
759 if (!memcmp(&data[1], "PNG", 3)) {
760 mime->setTo("image/png");
761 } else if (!memcmp(&data[1], "JPG", 3)) {
762 mime->setTo("image/jpeg");
763 } else if (!memcmp(&data[1], "-->", 3)) {
764 mime->setTo("text/plain");
765 } else {
766 return NULL;
767 }
768
769#if 0
770 uint8_t picType = data[4];
771 if (picType != 0x03) {
772 // Front Cover Art
773 it.next();
774 continue;
775 }
776#endif
777
778 size_t descLen = StringSize(&data[5], encoding);
779
780 *length = size - 5 - descLen;
781
782 return &data[5 + descLen];
783 }
784 }
785
786 return NULL;
787}
788
Andreas Huber43782d32010-01-19 13:35:27 -0800789bool ID3::parseV1(const sp<DataSource> &source) {
790 const size_t V1_TAG_SIZE = 128;
791
James Dongb1262a82010-11-16 14:04:54 -0800792 off64_t size;
793 if (source->getSize(&size) != OK || size < (off64_t)V1_TAG_SIZE) {
Andreas Huber43782d32010-01-19 13:35:27 -0800794 return false;
795 }
796
797 mData = (uint8_t *)malloc(V1_TAG_SIZE);
798 if (source->readAt(size - V1_TAG_SIZE, mData, V1_TAG_SIZE)
799 != (ssize_t)V1_TAG_SIZE) {
800 free(mData);
801 mData = NULL;
802
803 return false;
804 }
805
806 if (memcmp("TAG", mData, 3)) {
807 free(mData);
808 mData = NULL;
809
810 return false;
811 }
812
813 mSize = V1_TAG_SIZE;
814 mFirstFrameOffset = 3;
815
816 if (mData[V1_TAG_SIZE - 3] != 0) {
817 mVersion = ID3_V1;
818 } else {
819 mVersion = ID3_V1_1;
820 }
821
822 return true;
823}
Andreas Huberaee3c632010-01-11 15:35:19 -0800824
825} // namespace android