blob: aaaeeaf72e53e28669ccb0ef04883d3d63545f21 [file] [log] [blame]
Andreas Huberfc9ba092010-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
Dongwon Kangd91dc5a2017-10-10 00:07:09 -070023#include <media/DataSource.h>
Marco Nelissencec44d02018-06-17 22:21:09 -070024#include <media/MediaExtractorPluginApi.h>
25#include <media/MediaExtractorPluginHelper.h>
James Dongf1d5aa12012-02-06 23:46:37 -080026#include <media/stagefright/foundation/ADebug.h>
Dongwon Kang60761282017-10-09 11:16:48 -070027#include <media/stagefright/foundation/ByteUtils.h>
Andreas Huberfc9ba092010-01-11 15:35:19 -080028#include <utils/String8.h>
Marco Nelissen569e7f12010-02-09 07:40:10 -080029#include <byteswap.h>
Andreas Huberfc9ba092010-01-11 15:35:19 -080030
31namespace android {
32
Andreas Huber65997f02010-03-29 15:13:40 -070033static const size_t kMaxMetadataSize = 3 * 1024 * 1024;
34
Harish Mahendrakar37eca132020-01-31 11:31:23 -080035struct ID3::MemorySource : public DataSourceBase {
Andreas Huber14f76722013-01-15 09:04:18 -080036 MemorySource(const uint8_t *data, size_t size)
37 : mData(data),
38 mSize(size) {
39 }
40
41 virtual status_t initCheck() const {
42 return OK;
43 }
44
45 virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
Chih-Hung Hsieh3794b242018-12-11 13:55:06 -080046 off64_t available = (offset >= (off64_t)mSize) ? 0LL : mSize - offset;
Andreas Huber14f76722013-01-15 09:04:18 -080047
Colin Crossb4a7a2d2014-03-19 16:59:00 -070048 size_t copy = (available > (off64_t)size) ? size : available;
Andreas Huber14f76722013-01-15 09:04:18 -080049 memcpy(data, mData + offset, copy);
50
51 return copy;
52 }
53
54private:
55 const uint8_t *mData;
56 size_t mSize;
57
58 DISALLOW_EVIL_CONSTRUCTORS(MemorySource);
59};
60
Harish Mahendrakar37eca132020-01-31 11:31:23 -080061class ID3::DataSourceUnwrapper : public DataSourceBase {
Marco Nelissencec44d02018-06-17 22:21:09 -070062
63public:
64 explicit DataSourceUnwrapper(DataSourceHelper *sourcehelper) {
65 mSource = sourcehelper;
66 }
67 virtual status_t initCheck() const { return OK; }
68
69 // Returns the number of bytes read, or -1 on failure. It's not an error if
70 // this returns zero; it just means the given offset is equal to, or
71 // beyond, the end of the source.
72 virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
73 return mSource->readAt(offset, data, size);
74 }
75
76 // May return ERROR_UNSUPPORTED.
77 virtual status_t getSize(off64_t *size) {
78 return mSource->getSize(size);
79 }
80
81 virtual bool getUri(char * /*uriString*/, size_t /*bufferSize*/) {
82 return false;
83 }
84
85 virtual uint32_t flags() {
86 return 0;
87 }
88
89 virtual void close() {};
90private:
91 DataSourceHelper *mSource;
92};
93
94
95ID3::ID3(DataSourceHelper *sourcehelper, bool ignoreV1, off64_t offset)
96 : mIsValid(false),
97 mData(NULL),
98 mSize(0),
99 mFirstFrameOffset(0),
100 mVersion(ID3_UNKNOWN),
101 mRawSize(0) {
102 DataSourceUnwrapper source(sourcehelper);
103 mIsValid = parseV2(&source, offset);
104
105 if (!mIsValid && !ignoreV1) {
106 mIsValid = parseV1(&source);
107 }
108}
109
Andreas Huber14f76722013-01-15 09:04:18 -0800110ID3::ID3(const uint8_t *data, size_t size, bool ignoreV1)
111 : mIsValid(false),
112 mData(NULL),
113 mSize(0),
114 mFirstFrameOffset(0),
115 mVersion(ID3_UNKNOWN),
116 mRawSize(0) {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800117 MemorySource *source = new (std::nothrow) MemorySource(data, size);
Ray Essickc54432a2016-08-10 14:10:39 -0700118
119 if (source == NULL)
120 return;
Andreas Huber14f76722013-01-15 09:04:18 -0800121
Oscar Rydhé328abde2011-01-27 14:01:24 +0100122 mIsValid = parseV2(source, 0);
Andreas Huber83e58502010-01-19 13:35:27 -0800123
Dylan Powersffd6ffc2012-11-27 16:06:38 -0800124 if (!mIsValid && !ignoreV1) {
Andreas Huber83e58502010-01-19 13:35:27 -0800125 mIsValid = parseV1(source);
126 }
Marco Nelissen2a243f02018-01-30 08:29:57 -0800127 delete source;
Andreas Huberfc9ba092010-01-11 15:35:19 -0800128}
129
130ID3::~ID3() {
131 if (mData) {
132 free(mData);
133 mData = NULL;
134 }
135}
136
137bool ID3::isValid() const {
138 return mIsValid;
139}
140
141ID3::Version ID3::version() const {
142 return mVersion;
143}
144
Andreas Huberbebd11b2010-04-08 14:01:33 -0700145// static
146bool ID3::ParseSyncsafeInteger(const uint8_t encoded[4], size_t *x) {
147 *x = 0;
148 for (int32_t i = 0; i < 4; ++i) {
149 if (encoded[i] & 0x80) {
150 return false;
151 }
152
153 *x = ((*x) << 7) | encoded[i];
154 }
155
156 return true;
157}
158
Marco Nelissen2a243f02018-01-30 08:29:57 -0800159bool ID3::parseV2(DataSourceBase *source, off64_t offset) {
Andreas Huberbebd11b2010-04-08 14:01:33 -0700160struct id3_header {
161 char id[3];
162 uint8_t version_major;
163 uint8_t version_minor;
164 uint8_t flags;
165 uint8_t enc_size[4];
Andreas Huberfc9ba092010-01-11 15:35:19 -0800166 };
167
168 id3_header header;
169 if (source->readAt(
Oscar Rydhé328abde2011-01-27 14:01:24 +0100170 offset, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
Andreas Huberfc9ba092010-01-11 15:35:19 -0800171 return false;
172 }
173
174 if (memcmp(header.id, "ID3", 3)) {
175 return false;
176 }
177
178 if (header.version_major == 0xff || header.version_minor == 0xff) {
179 return false;
180 }
181
182 if (header.version_major == 2) {
183 if (header.flags & 0x3f) {
184 // We only support the 2 high bits, if any of the lower bits are
185 // set, we cannot guarantee to understand the tag format.
186 return false;
187 }
188
189 if (header.flags & 0x40) {
190 // No compression scheme has been decided yet, ignore the
191 // tag if compression is indicated.
192
193 return false;
194 }
195 } else if (header.version_major == 3) {
196 if (header.flags & 0x1f) {
197 // We only support the 3 high bits, if any of the lower bits are
198 // set, we cannot guarantee to understand the tag format.
199 return false;
200 }
Andreas Huberbebd11b2010-04-08 14:01:33 -0700201 } else if (header.version_major == 4) {
202 if (header.flags & 0x0f) {
203 // The lower 4 bits are undefined in this spec.
204 return false;
205 }
Andreas Huberfc9ba092010-01-11 15:35:19 -0800206 } else {
207 return false;
208 }
209
Andreas Huberbebd11b2010-04-08 14:01:33 -0700210 size_t size;
211 if (!ParseSyncsafeInteger(header.enc_size, &size)) {
212 return false;
Andreas Huberfc9ba092010-01-11 15:35:19 -0800213 }
214
Andreas Huber65997f02010-03-29 15:13:40 -0700215 if (size > kMaxMetadataSize) {
Colin Crossb4a7a2d2014-03-19 16:59:00 -0700216 ALOGE("skipping huge ID3 metadata of size %zu", size);
Andreas Huber65997f02010-03-29 15:13:40 -0700217 return false;
218 }
219
Andreas Huberfc9ba092010-01-11 15:35:19 -0800220 mData = (uint8_t *)malloc(size);
221
222 if (mData == NULL) {
223 return false;
224 }
225
226 mSize = size;
Andreas Huber14f76722013-01-15 09:04:18 -0800227 mRawSize = mSize + sizeof(header);
Andreas Huberfc9ba092010-01-11 15:35:19 -0800228
Oscar Rydhé328abde2011-01-27 14:01:24 +0100229 if (source->readAt(offset + sizeof(header), mData, mSize) != (ssize_t)mSize) {
Andreas Huberac994df2010-08-25 14:55:53 -0700230 free(mData);
231 mData = NULL;
232
Andreas Huberfc9ba092010-01-11 15:35:19 -0800233 return false;
234 }
235
Marco Nelissena7847b32020-04-17 17:57:18 -0700236 // first handle global unsynchronization
Ray Essick44c1ace2021-04-25 14:57:46 -0700237 bool hasGlobalUnsync = false;
Marco Nelissena7847b32020-04-17 17:57:18 -0700238 if (header.flags & 0x80) {
Ray Essick6626fd22021-04-28 19:08:44 -0700239 ALOGV("has Global unsynchronization");
Ray Essick44c1ace2021-04-25 14:57:46 -0700240 hasGlobalUnsync = true;
Ray Essick6626fd22021-04-28 19:08:44 -0700241 // we have to wait on applying global unsynchronization to V2.4 frames
242 // if we apply it now, the length information within any V2.4 frames goes bad
243 // Removing unsynchronization shrinks the buffer, but lengths (stored in safesync
244 // format) stored within the frame reflect "pre-shrinking" totals.
245
246 // we can (and should) apply the non-2.4 synch now.
247 if ( header.version_major != 4) {
248 ALOGV("Apply global unsync for non V2.4 frames");
249 removeUnsynchronization();
250 }
Andreas Huberfc9ba092010-01-11 15:35:19 -0800251 }
252
Marco Nelissena7847b32020-04-17 17:57:18 -0700253 // handle extended header, if present
Andreas Huberfc9ba092010-01-11 15:35:19 -0800254 mFirstFrameOffset = 0;
255 if (header.version_major == 3 && (header.flags & 0x40)) {
256 // Version 2.3 has an optional extended header.
257
258 if (mSize < 4) {
Andreas Huber83e58502010-01-19 13:35:27 -0800259 free(mData);
260 mData = NULL;
261
Andreas Huberfc9ba092010-01-11 15:35:19 -0800262 return false;
263 }
264
Marco Nelissena7847b32020-04-17 17:57:18 -0700265 // v2.3 does not have syncsafe integers
Wei Jiab3694ff2015-10-05 10:44:23 -0700266 size_t extendedHeaderSize = U32_AT(&mData[0]);
267 if (extendedHeaderSize > SIZE_MAX - 4) {
268 free(mData);
269 mData = NULL;
270 ALOGE("b/24623447, extendedHeaderSize is too large");
271 return false;
272 }
273 extendedHeaderSize += 4;
Andreas Huberfc9ba092010-01-11 15:35:19 -0800274
275 if (extendedHeaderSize > mSize) {
Andreas Huber83e58502010-01-19 13:35:27 -0800276 free(mData);
277 mData = NULL;
278
Andreas Huberfc9ba092010-01-11 15:35:19 -0800279 return false;
280 }
281
282 mFirstFrameOffset = extendedHeaderSize;
283
284 uint16_t extendedFlags = 0;
285 if (extendedHeaderSize >= 6) {
286 extendedFlags = U16_AT(&mData[4]);
287
288 if (extendedHeaderSize >= 10) {
289 size_t paddingSize = U32_AT(&mData[6]);
290
Wei Jiab3694ff2015-10-05 10:44:23 -0700291 if (paddingSize > SIZE_MAX - mFirstFrameOffset) {
292 ALOGE("b/24623447, paddingSize is too large");
293 }
294 if (paddingSize > mSize - mFirstFrameOffset) {
Andreas Huber83e58502010-01-19 13:35:27 -0800295 free(mData);
296 mData = NULL;
297
Andreas Huberfc9ba092010-01-11 15:35:19 -0800298 return false;
299 }
300
301 mSize -= paddingSize;
302 }
303
304 if (extendedFlags & 0x8000) {
Steve Block3856b092011-10-20 11:56:00 +0100305 ALOGV("have crc");
Andreas Huberfc9ba092010-01-11 15:35:19 -0800306 }
307 }
Andreas Huberbebd11b2010-04-08 14:01:33 -0700308 } else if (header.version_major == 4 && (header.flags & 0x40)) {
309 // Version 2.4 has an optional extended header, that's different
310 // from Version 2.3's...
311
312 if (mSize < 4) {
313 free(mData);
314 mData = NULL;
315
316 return false;
317 }
318
319 size_t ext_size;
320 if (!ParseSyncsafeInteger(mData, &ext_size)) {
321 free(mData);
322 mData = NULL;
323
324 return false;
325 }
326
327 if (ext_size < 6 || ext_size > mSize) {
328 free(mData);
329 mData = NULL;
330
331 return false;
332 }
333
334 mFirstFrameOffset = ext_size;
Andreas Huberfc9ba092010-01-11 15:35:19 -0800335 }
336
Marco Nelissena7847b32020-04-17 17:57:18 -0700337 // Handle any v2.4 per-frame unsynchronization
338 // The id3 spec isn't clear about what should happen if the global
339 // unsynchronization flag is combined with per-frame unsynchronization,
Ray Essick6626fd22021-04-28 19:08:44 -0700340 // or whether that's even allowed. We choose a "no more than 1 unsynchronization"
341 // semantic; the V2_4 unsynchronizer gets a copy of the global flag so it can handle
342 // this possible ambiquity.
343 //
Marco Nelissena7847b32020-04-17 17:57:18 -0700344 if (header.version_major == 4) {
345 void *copy = malloc(size);
346 if (copy == NULL) {
347 free(mData);
348 mData = NULL;
349 ALOGE("b/24623447, no more memory");
350 return false;
351 }
352
353 memcpy(copy, mData, size);
354
Ray Essick44c1ace2021-04-25 14:57:46 -0700355 bool success = removeUnsynchronizationV2_4(false /* iTunesHack */, hasGlobalUnsync);
Marco Nelissena7847b32020-04-17 17:57:18 -0700356 if (!success) {
357 memcpy(mData, copy, size);
358 mSize = size;
359
Ray Essick44c1ace2021-04-25 14:57:46 -0700360 success = removeUnsynchronizationV2_4(true /* iTunesHack */, hasGlobalUnsync);
Marco Nelissena7847b32020-04-17 17:57:18 -0700361
362 if (success) {
363 ALOGV("Had to apply the iTunes hack to parse this ID3 tag");
364 }
365 }
366
367 free(copy);
368 copy = NULL;
369
370 if (!success) {
371 free(mData);
372 mData = NULL;
373
374 return false;
375 }
376 }
377
378
Andreas Huberfc9ba092010-01-11 15:35:19 -0800379 if (header.version_major == 2) {
380 mVersion = ID3_V2_2;
Andreas Huberbebd11b2010-04-08 14:01:33 -0700381 } else if (header.version_major == 3) {
Andreas Huberfc9ba092010-01-11 15:35:19 -0800382 mVersion = ID3_V2_3;
Andreas Huberbebd11b2010-04-08 14:01:33 -0700383 } else {
384 CHECK_EQ(header.version_major, 4);
385 mVersion = ID3_V2_4;
Andreas Huberfc9ba092010-01-11 15:35:19 -0800386 }
387
388 return true;
389}
390
391void ID3::removeUnsynchronization() {
Robert Shihf9d87cc2018-05-09 15:16:17 -0700392
393 // This file has "unsynchronization", so we have to replace occurrences
394 // of 0xff 0x00 with just 0xff in order to get the real data.
395
396 size_t writeOffset = 1;
397 for (size_t readOffset = 1; readOffset < mSize; ++readOffset) {
398 if (mData[readOffset - 1] == 0xff && mData[readOffset] == 0x00) {
399 continue;
Andreas Huberfc9ba092010-01-11 15:35:19 -0800400 }
Robert Shihf9d87cc2018-05-09 15:16:17 -0700401 // Only move data if there's actually something to move.
402 // This handles the special case of the data being only [0xff, 0x00]
403 // which should be converted to just 0xff if unsynchronization is on.
404 mData[writeOffset++] = mData[readOffset];
Andreas Huberfc9ba092010-01-11 15:35:19 -0800405 }
Robert Shihf9d87cc2018-05-09 15:16:17 -0700406
407 if (writeOffset < mSize) {
408 mSize = writeOffset;
409 }
410
Andreas Huberfc9ba092010-01-11 15:35:19 -0800411}
412
Andreas Huberac994df2010-08-25 14:55:53 -0700413static void WriteSyncsafeInteger(uint8_t *dst, size_t x) {
414 for (size_t i = 0; i < 4; ++i) {
415 dst[3 - i] = (x & 0x7f);
416 x >>= 7;
417 }
418}
419
Ray Essick44c1ace2021-04-25 14:57:46 -0700420bool ID3::removeUnsynchronizationV2_4(bool iTunesHack, bool hasGlobalUnsync) {
Andreas Huberac994df2010-08-25 14:55:53 -0700421 size_t oldSize = mSize;
422
Marco Nelissena7847b32020-04-17 17:57:18 -0700423 size_t offset = mFirstFrameOffset;
Wei Jia09da8692015-08-12 10:41:00 -0700424 while (mSize >= 10 && offset <= mSize - 10) {
Andreas Huberac994df2010-08-25 14:55:53 -0700425 if (!memcmp(&mData[offset], "\0\0\0\0", 4)) {
426 break;
427 }
428
429 size_t dataSize;
Andreas Huber428d96d2010-12-14 09:49:29 -0800430 if (iTunesHack) {
431 dataSize = U32_AT(&mData[offset + 4]);
432 } else if (!ParseSyncsafeInteger(&mData[offset + 4], &dataSize)) {
Andreas Huberac994df2010-08-25 14:55:53 -0700433 return false;
434 }
435
Wei Jia09da8692015-08-12 10:41:00 -0700436 if (dataSize > mSize - 10 - offset) {
Andreas Huberac994df2010-08-25 14:55:53 -0700437 return false;
438 }
439
440 uint16_t flags = U16_AT(&mData[offset + 8]);
441 uint16_t prevFlags = flags;
442
443 if (flags & 1) {
444 // Strip data length indicator
445
Neel Mehtac37f7f62015-08-14 17:38:48 -0700446 if (mSize < 14 || mSize - 14 < offset || dataSize < 4) {
Wei Jia09da8692015-08-12 10:41:00 -0700447 return false;
448 }
Andreas Huberac994df2010-08-25 14:55:53 -0700449 memmove(&mData[offset + 10], &mData[offset + 14], mSize - offset - 14);
450 mSize -= 4;
451 dataSize -= 4;
452
453 flags &= ~1;
454 }
455
Ray Essick6626fd22021-04-28 19:08:44 -0700456 ALOGV("hasglobal %d flags&2 %d", hasGlobalUnsync, flags&2);
457 if (hasGlobalUnsync && !(flags & 2)) {
458 ALOGV("OOPS: global unsync set, but per-frame NOT set; removing unsync anyway");
459 }
460 if ((hasGlobalUnsync || (flags & 2)) && (dataSize >= 2)) {
Marco Nelissena7847b32020-04-17 17:57:18 -0700461 // This frame has "unsynchronization", so we have to replace occurrences
Marco Nelissen72a43b62013-06-17 16:14:39 -0700462 // of 0xff 0x00 with just 0xff in order to get the real data.
Andreas Huberac994df2010-08-25 14:55:53 -0700463
Marco Nelissen72a43b62013-06-17 16:14:39 -0700464 size_t readOffset = offset + 11;
465 size_t writeOffset = offset + 11;
Andreas Huberac994df2010-08-25 14:55:53 -0700466 for (size_t i = 0; i + 1 < dataSize; ++i) {
Marco Nelissen72a43b62013-06-17 16:14:39 -0700467 if (mData[readOffset - 1] == 0xff
468 && mData[readOffset] == 0x00) {
469 ++readOffset;
Andreas Huberac994df2010-08-25 14:55:53 -0700470 --mSize;
471 --dataSize;
472 }
Marco Nelissen3e702962017-10-09 14:59:43 -0700473 if (i + 1 < dataSize) {
474 // Only move data if there's actually something to move.
475 // This handles the special case of the data being only [0xff, 0x00]
476 // which should be converted to just 0xff if unsynchronization is on.
477 mData[writeOffset++] = mData[readOffset++];
478 }
Andreas Huberac994df2010-08-25 14:55:53 -0700479 }
Marco Nelissen72a43b62013-06-17 16:14:39 -0700480 // move the remaining data following this frame
Marco Nelissend1c19c52017-03-10 11:28:44 -0800481 if (readOffset <= oldSize) {
482 memmove(&mData[writeOffset], &mData[readOffset], oldSize - readOffset);
483 } else {
484 ALOGE("b/34618607 (%zu %zu %zu %zu)", readOffset, writeOffset, oldSize, mSize);
485 android_errorWriteLog(0x534e4554, "34618607");
486 }
Andreas Huberac994df2010-08-25 14:55:53 -0700487 }
Marco Nelissend1c19c52017-03-10 11:28:44 -0800488 flags &= ~2;
Andreas Huber428d96d2010-12-14 09:49:29 -0800489 if (flags != prevFlags || iTunesHack) {
Andreas Huberac994df2010-08-25 14:55:53 -0700490 WriteSyncsafeInteger(&mData[offset + 4], dataSize);
491 mData[offset + 8] = flags >> 8;
492 mData[offset + 9] = flags & 0xff;
493 }
494
495 offset += 10 + dataSize;
496 }
497
498 memset(&mData[mSize], 0, oldSize - mSize);
499
500 return true;
501}
502
Andreas Huberfc9ba092010-01-11 15:35:19 -0800503ID3::Iterator::Iterator(const ID3 &parent, const char *id)
504 : mParent(parent),
505 mID(NULL),
506 mOffset(mParent.mFirstFrameOffset),
507 mFrameData(NULL),
508 mFrameSize(0) {
509 if (id) {
510 mID = strdup(id);
511 }
512
513 findFrame();
514}
515
516ID3::Iterator::~Iterator() {
517 if (mID) {
518 free(mID);
519 mID = NULL;
520 }
521}
522
523bool ID3::Iterator::done() const {
524 return mFrameData == NULL;
525}
526
527void ID3::Iterator::next() {
528 if (mFrameData == NULL) {
529 return;
530 }
531
532 mOffset += mFrameSize;
533
534 findFrame();
535}
536
537void ID3::Iterator::getID(String8 *id) const {
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000538 *id = "";
Andreas Huberfc9ba092010-01-11 15:35:19 -0800539
540 if (mFrameData == NULL) {
541 return;
542 }
543
544 if (mParent.mVersion == ID3_V2_2) {
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000545 *id = String8((const char *)&mParent.mData[mOffset], 3);
Andreas Huberbebd11b2010-04-08 14:01:33 -0700546 } else if (mParent.mVersion == ID3_V2_3 || mParent.mVersion == ID3_V2_4) {
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000547 *id = String8((const char *)&mParent.mData[mOffset], 4);
Andreas Huber83e58502010-01-19 13:35:27 -0800548 } else {
549 CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
550
551 switch (mOffset) {
552 case 3:
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000553 *id = "TT2";
Andreas Huber83e58502010-01-19 13:35:27 -0800554 break;
555 case 33:
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000556 *id = "TP1";
Andreas Huber83e58502010-01-19 13:35:27 -0800557 break;
558 case 63:
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000559 *id = "TAL";
Andreas Huber83e58502010-01-19 13:35:27 -0800560 break;
561 case 93:
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000562 *id = "TYE";
Andreas Huber83e58502010-01-19 13:35:27 -0800563 break;
564 case 97:
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000565 *id = "COM";
Andreas Huber83e58502010-01-19 13:35:27 -0800566 break;
567 case 126:
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000568 *id = "TRK";
Andreas Huber83e58502010-01-19 13:35:27 -0800569 break;
570 case 127:
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000571 *id = "TCO";
Andreas Huber83e58502010-01-19 13:35:27 -0800572 break;
573 default:
574 CHECK(!"should not be here.");
575 break;
576 }
Andreas Huberfc9ba092010-01-11 15:35:19 -0800577 }
578}
579
Andreas Huberfc9ba092010-01-11 15:35:19 -0800580
Marco Nelissenb636abd2012-03-19 13:49:43 -0700581// the 2nd argument is used to get the data following the \0 in a comment field
582void ID3::Iterator::getString(String8 *id, String8 *comment) const {
583 getstring(id, false);
584 if (comment != NULL) {
585 getstring(comment, true);
586 }
587}
588
589// comment fields (COM/COMM) contain an initial short descriptor, followed by \0,
590// followed by more data. The data following the \0 can be retrieved by setting
591// "otherdata" to true.
592void ID3::Iterator::getstring(String8 *id, bool otherdata) const {
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000593 *id = "";
Andreas Huberfc9ba092010-01-11 15:35:19 -0800594
Marco Nelissenb636abd2012-03-19 13:49:43 -0700595 const uint8_t *frameData = mFrameData;
596 if (frameData == NULL) {
Andreas Huberfc9ba092010-01-11 15:35:19 -0800597 return;
598 }
599
Marco Nelissenb636abd2012-03-19 13:49:43 -0700600 uint8_t encoding = *frameData;
601
Andreas Huber83e58502010-01-19 13:35:27 -0800602 if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) {
603 if (mOffset == 126 || mOffset == 127) {
604 // Special treatment for the track number and genre.
605 char tmp[16];
George Burgess IV2c9cb622016-02-29 13:39:17 -0800606 snprintf(tmp, sizeof(tmp), "%d", (int)*frameData);
Andreas Huber83e58502010-01-19 13:35:27 -0800607
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000608 *id = tmp;
Andreas Huber83e58502010-01-19 13:35:27 -0800609 return;
610 }
611
Marco Nelissen544ad2b2013-11-13 14:18:21 -0800612 // this is supposed to be ISO-8859-1, but pass it up as-is to the caller, who will figure
613 // out the real encoding
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000614 *id = String8((const char*)frameData, mFrameSize);
Andreas Huber83e58502010-01-19 13:35:27 -0800615 return;
616 }
617
Wei Jia3bf1e0f2015-08-16 17:41:50 -0700618 if (mFrameSize < getHeaderLength() + 1) {
619 return;
620 }
Andreas Huberfc9ba092010-01-11 15:35:19 -0800621 size_t n = mFrameSize - getHeaderLength() - 1;
Marco Nelissenb636abd2012-03-19 13:49:43 -0700622 if (otherdata) {
Roger1 Jonssone1e79172016-09-21 09:06:34 +0200623 if (n < 5) {
624 return;
625 }
Marco Nelissenb636abd2012-03-19 13:49:43 -0700626 // skip past the encoding, language, and the 0 separator
627 frameData += 4;
628 int32_t i = n - 4;
629 while(--i >= 0 && *++frameData != 0) ;
630 int skipped = (frameData - mFrameData);
Andreas Huber14f76722013-01-15 09:04:18 -0800631 if (skipped >= (int)n) {
Marco Nelissenb636abd2012-03-19 13:49:43 -0700632 return;
633 }
634 n -= skipped;
635 }
Andreas Huberfc9ba092010-01-11 15:35:19 -0800636
Ray Essickd2b12622016-08-11 10:48:51 -0700637 if (n <= 0) {
638 return;
639 }
640
Marco Nelissenb636abd2012-03-19 13:49:43 -0700641 if (encoding == 0x00) {
Marco Nelissen544ad2b2013-11-13 14:18:21 -0800642 // supposedly ISO 8859-1
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000643 *id = String8((const char*)frameData + 1, n);
Marco Nelissenb636abd2012-03-19 13:49:43 -0700644 } else if (encoding == 0x03) {
Marco Nelissen544ad2b2013-11-13 14:18:21 -0800645 // supposedly UTF-8
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000646 *id = String8((const char *)(frameData + 1), n);
Marco Nelissenb636abd2012-03-19 13:49:43 -0700647 } else if (encoding == 0x02) {
Marco Nelissen544ad2b2013-11-13 14:18:21 -0800648 // supposedly UTF-16 BE, no byte order mark.
Andreas Huberbebd11b2010-04-08 14:01:33 -0700649 // API wants number of characters, not number of bytes...
650 int len = n / 2;
Marco Nelissenb636abd2012-03-19 13:49:43 -0700651 const char16_t *framedata = (const char16_t *) (frameData + 1);
Andreas Huberbebd11b2010-04-08 14:01:33 -0700652 char16_t *framedatacopy = NULL;
653#if BYTE_ORDER == LITTLE_ENDIAN
Ray Essickc54432a2016-08-10 14:10:39 -0700654 if (len > 0) {
655 framedatacopy = new (std::nothrow) char16_t[len];
Ray Essickd2b12622016-08-11 10:48:51 -0700656 if (framedatacopy == NULL) {
657 return;
Ray Essickc54432a2016-08-10 14:10:39 -0700658 }
Ray Essickd2b12622016-08-11 10:48:51 -0700659 for (int i = 0; i < len; i++) {
660 framedatacopy[i] = bswap_16(framedata[i]);
661 }
662 framedata = framedatacopy;
Andreas Huberbebd11b2010-04-08 14:01:33 -0700663 }
Andreas Huberbebd11b2010-04-08 14:01:33 -0700664#endif
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000665 *id = String8(framedata, len);
Andreas Huberbebd11b2010-04-08 14:01:33 -0700666 if (framedatacopy != NULL) {
667 delete[] framedatacopy;
668 }
Marco Nelissen544ad2b2013-11-13 14:18:21 -0800669 } else if (encoding == 0x01) {
Andreas Huberfc9ba092010-01-11 15:35:19 -0800670 // UCS-2
Andreas Huber08e7eb92010-01-19 13:52:06 -0800671 // API wants number of characters, not number of bytes...
Marco Nelissen5a2621a2010-02-08 15:04:23 -0800672 int len = n / 2;
Marco Nelissen3762e062018-06-01 10:48:25 -0700673 if (len == 0) {
674 return;
675 }
Marco Nelissenb636abd2012-03-19 13:49:43 -0700676 const char16_t *framedata = (const char16_t *) (frameData + 1);
Marco Nelissen5a2621a2010-02-08 15:04:23 -0800677 char16_t *framedatacopy = NULL;
678 if (*framedata == 0xfffe) {
Ray Essickd2b12622016-08-11 10:48:51 -0700679 // endianness marker != host endianness, convert & skip
680 if (len <= 1) {
681 return; // nothing after the marker
Marco Nelissen5a2621a2010-02-08 15:04:23 -0800682 }
Ray Essickd2b12622016-08-11 10:48:51 -0700683 framedatacopy = new (std::nothrow) char16_t[len];
684 if (framedatacopy == NULL) {
685 return;
686 }
687 for (int i = 0; i < len; i++) {
688 framedatacopy[i] = bswap_16(framedata[i]);
689 }
690 framedata = framedatacopy;
691 // and skip over the marker
692 framedata++;
693 len--;
694 } else if (*framedata == 0xfeff) {
695 // endianness marker == host endianness, skip it
696 if (len <= 1) {
697 return; // nothing after the marker
698 }
Marco Nelissen5a2621a2010-02-08 15:04:23 -0800699 framedata++;
700 len--;
701 }
Marco Nelissen544ad2b2013-11-13 14:18:21 -0800702
703 // check if the resulting data consists entirely of 8-bit values
704 bool eightBit = true;
705 for (int i = 0; i < len; i++) {
706 if (framedata[i] > 0xff) {
707 eightBit = false;
708 break;
709 }
710 }
Ray Essickd2b12622016-08-11 10:48:51 -0700711 if (eightBit) {
Marco Nelissen544ad2b2013-11-13 14:18:21 -0800712 // collapse to 8 bit, then let the media scanner client figure out the real encoding
Ray Essickc54432a2016-08-10 14:10:39 -0700713 char *frame8 = new (std::nothrow) char[len];
714 if (frame8 != NULL) {
715 for (int i = 0; i < len; i++) {
716 frame8[i] = framedata[i];
717 }
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000718 *id = String8(frame8, len);
Ray Essickc54432a2016-08-10 14:10:39 -0700719 delete [] frame8;
Ray Essickd2b12622016-08-11 10:48:51 -0700720 } else {
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000721 *id = String8(framedata, len);
Marco Nelissen544ad2b2013-11-13 14:18:21 -0800722 }
Marco Nelissen544ad2b2013-11-13 14:18:21 -0800723 } else {
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000724 *id = String8(framedata, len);
Marco Nelissen544ad2b2013-11-13 14:18:21 -0800725 }
726
Marco Nelissen5a2621a2010-02-08 15:04:23 -0800727 if (framedatacopy != NULL) {
728 delete[] framedatacopy;
729 }
Andreas Huberfc9ba092010-01-11 15:35:19 -0800730 }
731}
732
733const uint8_t *ID3::Iterator::getData(size_t *length) const {
734 *length = 0;
735
736 if (mFrameData == NULL) {
737 return NULL;
738 }
739
Joshua J. Drakebe7b5e22015-08-15 08:31:32 -0500740 // Prevent integer underflow
741 if (mFrameSize < getHeaderLength()) {
742 return NULL;
743 }
744
Andreas Huberfc9ba092010-01-11 15:35:19 -0800745 *length = mFrameSize - getHeaderLength();
746
747 return mFrameData;
748}
749
750size_t ID3::Iterator::getHeaderLength() const {
751 if (mParent.mVersion == ID3_V2_2) {
752 return 6;
Andreas Huberbebd11b2010-04-08 14:01:33 -0700753 } else if (mParent.mVersion == ID3_V2_3 || mParent.mVersion == ID3_V2_4) {
Andreas Huberfc9ba092010-01-11 15:35:19 -0800754 return 10;
Andreas Huber83e58502010-01-19 13:35:27 -0800755 } else {
756 CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
757 return 0;
Andreas Huberfc9ba092010-01-11 15:35:19 -0800758 }
759}
760
761void ID3::Iterator::findFrame() {
762 for (;;) {
763 mFrameData = NULL;
764 mFrameSize = 0;
765
766 if (mParent.mVersion == ID3_V2_2) {
767 if (mOffset + 6 > mParent.mSize) {
768 return;
769 }
770
771 if (!memcmp(&mParent.mData[mOffset], "\0\0\0", 3)) {
772 return;
773 }
774
775 mFrameSize =
776 (mParent.mData[mOffset + 3] << 16)
777 | (mParent.mData[mOffset + 4] << 8)
778 | mParent.mData[mOffset + 5];
779
Marco Nelissenba6e9822015-01-15 14:11:19 -0800780 if (mFrameSize == 0) {
781 return;
782 }
783 mFrameSize += 6; // add tag id and size field
Andreas Huberfc9ba092010-01-11 15:35:19 -0800784
Joshua J. Drakec580c832015-08-15 08:17:03 -0500785 // Prevent integer overflow in validation
786 if (SIZE_MAX - mOffset <= mFrameSize) {
787 return;
788 }
789
Andreas Huberfc9ba092010-01-11 15:35:19 -0800790 if (mOffset + mFrameSize > mParent.mSize) {
Colin Crossb4a7a2d2014-03-19 16:59:00 -0700791 ALOGV("partial frame at offset %zu (size = %zu, bytes-remaining = %zu)",
792 mOffset, mFrameSize, mParent.mSize - mOffset - (size_t)6);
Andreas Huberfc9ba092010-01-11 15:35:19 -0800793 return;
794 }
795
796 mFrameData = &mParent.mData[mOffset + 6];
797
798 if (!mID) {
799 break;
800 }
801
802 char id[4];
803 memcpy(id, &mParent.mData[mOffset], 3);
804 id[3] = '\0';
805
806 if (!strcmp(id, mID)) {
807 break;
808 }
Andreas Huberbebd11b2010-04-08 14:01:33 -0700809 } else if (mParent.mVersion == ID3_V2_3
810 || mParent.mVersion == ID3_V2_4) {
Andreas Huberfc9ba092010-01-11 15:35:19 -0800811 if (mOffset + 10 > mParent.mSize) {
812 return;
813 }
814
815 if (!memcmp(&mParent.mData[mOffset], "\0\0\0\0", 4)) {
816 return;
817 }
818
Joshua J. Drakec580c832015-08-15 08:17:03 -0500819 size_t baseSize = 0;
Andreas Huberbebd11b2010-04-08 14:01:33 -0700820 if (mParent.mVersion == ID3_V2_4) {
821 if (!ParseSyncsafeInteger(
822 &mParent.mData[mOffset + 4], &baseSize)) {
823 return;
824 }
825 } else {
826 baseSize = U32_AT(&mParent.mData[mOffset + 4]);
827 }
828
Marco Nelissen3992a1c2020-12-17 20:16:22 +0000829 if (baseSize == 0) {
830 return;
831 }
832
Joshua J. Drakec580c832015-08-15 08:17:03 -0500833 // Prevent integer overflow when adding
834 if (SIZE_MAX - 10 <= baseSize) {
835 return;
836 }
837
Marco Nelissenba6e9822015-01-15 14:11:19 -0800838 mFrameSize = 10 + baseSize; // add tag id, size field and flags
Andreas Huberfc9ba092010-01-11 15:35:19 -0800839
Joshua J. Drakec580c832015-08-15 08:17:03 -0500840 // Prevent integer overflow in validation
841 if (SIZE_MAX - mOffset <= mFrameSize) {
842 return;
843 }
Andreas Huberfc9ba092010-01-11 15:35:19 -0800844
845 if (mOffset + mFrameSize > mParent.mSize) {
Colin Crossb4a7a2d2014-03-19 16:59:00 -0700846 ALOGV("partial frame at offset %zu (size = %zu, bytes-remaining = %zu)",
847 mOffset, mFrameSize, mParent.mSize - mOffset - (size_t)10);
Andreas Huberfc9ba092010-01-11 15:35:19 -0800848 return;
849 }
850
Andreas Huberbebd11b2010-04-08 14:01:33 -0700851 uint16_t flags = U16_AT(&mParent.mData[mOffset + 8]);
852
Andreas Huberac994df2010-08-25 14:55:53 -0700853 if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000c))
Andreas Huberbebd11b2010-04-08 14:01:33 -0700854 || (mParent.mVersion == ID3_V2_3 && (flags & 0x00c0))) {
Andreas Huberac994df2010-08-25 14:55:53 -0700855 // Compression or encryption are not supported at this time.
856 // Per-frame unsynchronization and data-length indicator
857 // have already been taken care of.
Andreas Huberbebd11b2010-04-08 14:01:33 -0700858
Steve Block3856b092011-10-20 11:56:00 +0100859 ALOGV("Skipping unsupported frame (compression, encryption "
Andreas Huberbebd11b2010-04-08 14:01:33 -0700860 "or per-frame unsynchronization flagged");
861
862 mOffset += mFrameSize;
863 continue;
864 }
865
Andreas Huberfc9ba092010-01-11 15:35:19 -0800866 mFrameData = &mParent.mData[mOffset + 10];
867
868 if (!mID) {
869 break;
870 }
871
872 char id[5];
873 memcpy(id, &mParent.mData[mOffset], 4);
874 id[4] = '\0';
875
876 if (!strcmp(id, mID)) {
877 break;
878 }
Andreas Huber83e58502010-01-19 13:35:27 -0800879 } else {
880 CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
881
882 if (mOffset >= mParent.mSize) {
883 return;
884 }
885
886 mFrameData = &mParent.mData[mOffset];
887
888 switch (mOffset) {
889 case 3:
890 case 33:
891 case 63:
892 mFrameSize = 30;
893 break;
894 case 93:
895 mFrameSize = 4;
896 break;
897 case 97:
898 if (mParent.mVersion == ID3_V1) {
899 mFrameSize = 30;
900 } else {
901 mFrameSize = 29;
902 }
903 break;
904 case 126:
905 mFrameSize = 1;
906 break;
907 case 127:
908 mFrameSize = 1;
909 break;
910 default:
911 CHECK(!"Should not be here, invalid offset.");
912 break;
913 }
914
915 if (!mID) {
916 break;
917 }
918
919 String8 id;
920 getID(&id);
921
922 if (id == mID) {
923 break;
924 }
Andreas Huberfc9ba092010-01-11 15:35:19 -0800925 }
926
927 mOffset += mFrameSize;
928 }
929}
930
Ray Essickebb726f2016-10-27 09:07:40 -0700931// return includes terminator; if unterminated, returns > limit
932static size_t StringSize(const uint8_t *start, size_t limit, uint8_t encoding) {
933
Andreas Huberbebd11b2010-04-08 14:01:33 -0700934 if (encoding == 0x00 || encoding == 0x03) {
935 // ISO 8859-1 or UTF-8
Ray Essickebb726f2016-10-27 09:07:40 -0700936 return strnlen((const char *)start, limit) + 1;
Andreas Huberfc9ba092010-01-11 15:35:19 -0800937 }
938
939 // UCS-2
940 size_t n = 0;
Ray Essickebb726f2016-10-27 09:07:40 -0700941 while ((n+1 < limit) && (start[n] != '\0' || start[n + 1] != '\0')) {
Andreas Huberfc9ba092010-01-11 15:35:19 -0800942 n += 2;
943 }
Ray Essickebb726f2016-10-27 09:07:40 -0700944 n += 2;
945 return n;
Andreas Huberfc9ba092010-01-11 15:35:19 -0800946}
947
948const void *
949ID3::getAlbumArt(size_t *length, String8 *mime) const {
950 *length = 0;
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000951 *mime = "";
Andreas Huberfc9ba092010-01-11 15:35:19 -0800952
Andreas Huberbebd11b2010-04-08 14:01:33 -0700953 Iterator it(
954 *this,
955 (mVersion == ID3_V2_3 || mVersion == ID3_V2_4) ? "APIC" : "PIC");
Andreas Huberfc9ba092010-01-11 15:35:19 -0800956
957 while (!it.done()) {
958 size_t size;
959 const uint8_t *data = it.getData(&size);
Joshua J. Drakebe7b5e22015-08-15 08:31:32 -0500960 if (!data) {
961 return NULL;
962 }
Andreas Huberfc9ba092010-01-11 15:35:19 -0800963
Andreas Huberbebd11b2010-04-08 14:01:33 -0700964 if (mVersion == ID3_V2_3 || mVersion == ID3_V2_4) {
Andreas Huberfc9ba092010-01-11 15:35:19 -0800965 uint8_t encoding = data[0];
Ray Essickebb726f2016-10-27 09:07:40 -0700966 size_t consumed = 1;
967
968 // *always* in an 8-bit encoding
969 size_t mimeLen = StringSize(&data[consumed], size - consumed, 0x00);
970 if (mimeLen > size - consumed) {
971 ALOGW("bogus album art size: mime");
972 return NULL;
973 }
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +0000974 *mime = (const char *)&data[consumed];
Ray Essickebb726f2016-10-27 09:07:40 -0700975 consumed += mimeLen;
Andreas Huberfc9ba092010-01-11 15:35:19 -0800976
Andreas Huberfc9ba092010-01-11 15:35:19 -0800977#if 0
Ray Essickebb726f2016-10-27 09:07:40 -0700978 uint8_t picType = data[consumed];
Andreas Huberfc9ba092010-01-11 15:35:19 -0800979 if (picType != 0x03) {
980 // Front Cover Art
981 it.next();
982 continue;
983 }
984#endif
985
Ray Essickebb726f2016-10-27 09:07:40 -0700986 consumed++;
987 if (consumed >= size) {
988 ALOGW("bogus album art size: pic type");
Marco Nelissenf26400c2015-08-04 16:49:28 -0700989 return NULL;
990 }
Andreas Huberfc9ba092010-01-11 15:35:19 -0800991
Ray Essickebb726f2016-10-27 09:07:40 -0700992 size_t descLen = StringSize(&data[consumed], size - consumed, encoding);
993 consumed += descLen;
994
995 if (consumed >= size) {
996 ALOGW("bogus album art size: description");
997 return NULL;
998 }
999
1000 *length = size - consumed;
1001
1002 return &data[consumed];
Andreas Huberfc9ba092010-01-11 15:35:19 -08001003 } else {
1004 uint8_t encoding = data[0];
1005
Ray Essickebb726f2016-10-27 09:07:40 -07001006 if (size <= 5) {
1007 return NULL;
1008 }
1009
Andreas Huberfc9ba092010-01-11 15:35:19 -08001010 if (!memcmp(&data[1], "PNG", 3)) {
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +00001011 *mime = "image/png";
Andreas Huberfc9ba092010-01-11 15:35:19 -08001012 } else if (!memcmp(&data[1], "JPG", 3)) {
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +00001013 *mime = "image/jpeg";
Andreas Huberfc9ba092010-01-11 15:35:19 -08001014 } else if (!memcmp(&data[1], "-->", 3)) {
Tomasz Wasilczyk9e975d92023-08-23 22:11:17 +00001015 *mime = "text/plain";
Andreas Huberfc9ba092010-01-11 15:35:19 -08001016 } else {
1017 return NULL;
1018 }
1019
1020#if 0
1021 uint8_t picType = data[4];
1022 if (picType != 0x03) {
1023 // Front Cover Art
1024 it.next();
1025 continue;
1026 }
1027#endif
1028
Ray Essickebb726f2016-10-27 09:07:40 -07001029 size_t descLen = StringSize(&data[5], size - 5, encoding);
1030 if (descLen > size - 5) {
1031 return NULL;
1032 }
Andreas Huberfc9ba092010-01-11 15:35:19 -08001033
1034 *length = size - 5 - descLen;
1035
1036 return &data[5 + descLen];
1037 }
1038 }
1039
1040 return NULL;
1041}
1042
Marco Nelissen2a243f02018-01-30 08:29:57 -08001043bool ID3::parseV1(DataSourceBase *source) {
Andreas Huber83e58502010-01-19 13:35:27 -08001044 const size_t V1_TAG_SIZE = 128;
1045
James Dongc7fc37a2010-11-16 14:04:54 -08001046 off64_t size;
1047 if (source->getSize(&size) != OK || size < (off64_t)V1_TAG_SIZE) {
Andreas Huber83e58502010-01-19 13:35:27 -08001048 return false;
1049 }
1050
1051 mData = (uint8_t *)malloc(V1_TAG_SIZE);
1052 if (source->readAt(size - V1_TAG_SIZE, mData, V1_TAG_SIZE)
1053 != (ssize_t)V1_TAG_SIZE) {
1054 free(mData);
1055 mData = NULL;
1056
1057 return false;
1058 }
1059
1060 if (memcmp("TAG", mData, 3)) {
1061 free(mData);
1062 mData = NULL;
1063
1064 return false;
1065 }
1066
1067 mSize = V1_TAG_SIZE;
1068 mFirstFrameOffset = 3;
1069
1070 if (mData[V1_TAG_SIZE - 3] != 0) {
1071 mVersion = ID3_V1;
1072 } else {
1073 mVersion = ID3_V1_1;
1074 }
1075
1076 return true;
1077}
Andreas Huberfc9ba092010-01-11 15:35:19 -08001078
1079} // namespace android