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