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 "OggExtractor" 19 #include <utils/Log.h> 20 21 #include "include/OggExtractor.h" 22 23 #include <cutils/properties.h> 24 #include <media/stagefright/foundation/ADebug.h> 25 #include <media/stagefright/DataSource.h> 26 #include <media/stagefright/MediaBuffer.h> 27 #include <media/stagefright/MediaBufferGroup.h> 28 #include <media/stagefright/MediaDefs.h> 29 #include <media/stagefright/MediaErrors.h> 30 #include <media/stagefright/MediaSource.h> 31 #include <media/stagefright/MetaData.h> 32 #include <media/stagefright/Utils.h> 33 #include <utils/String8.h> 34 35 extern "C" { 36 #include <Tremolo/codec_internal.h> 37 38 int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb); 39 int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb); 40 int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb); 41 } 42 43 namespace android { 44 45 struct OggSource : public MediaSource { 46 OggSource(const sp<OggExtractor> &extractor); 47 48 virtual sp<MetaData> getFormat(); 49 50 virtual status_t start(MetaData *params = NULL); 51 virtual status_t stop(); 52 53 virtual status_t read( 54 MediaBuffer **buffer, const ReadOptions *options = NULL); 55 56 protected: 57 virtual ~OggSource(); 58 59 private: 60 sp<OggExtractor> mExtractor; 61 bool mStarted; 62 63 OggSource(const OggSource &); 64 OggSource &operator=(const OggSource &); 65 }; 66 67 struct MyVorbisExtractor { 68 MyVorbisExtractor(const sp<DataSource> &source); 69 virtual ~MyVorbisExtractor(); 70 71 sp<MetaData> getFormat() const; 72 73 // Returns an approximate bitrate in bits per second. 74 uint64_t approxBitrate(); 75 76 status_t seekToTime(int64_t timeUs); 77 status_t seekToOffset(off64_t offset); 78 status_t readNextPacket(MediaBuffer **buffer); 79 80 status_t init(); 81 82 sp<MetaData> getFileMetaData() { return mFileMeta; } 83 84 private: 85 struct Page { 86 uint64_t mGranulePosition; 87 uint32_t mSerialNo; 88 uint32_t mPageNo; 89 uint8_t mFlags; 90 uint8_t mNumSegments; 91 uint8_t mLace[255]; 92 }; 93 94 struct TOCEntry { 95 off64_t mPageOffset; 96 int64_t mTimeUs; 97 }; 98 99 sp<DataSource> mSource; 100 off64_t mOffset; 101 Page mCurrentPage; 102 uint64_t mPrevGranulePosition; 103 size_t mCurrentPageSize; 104 bool mFirstPacketInPage; 105 uint64_t mCurrentPageSamples; 106 size_t mNextLaceIndex; 107 108 off64_t mFirstDataOffset; 109 110 vorbis_info mVi; 111 vorbis_comment mVc; 112 113 sp<MetaData> mMeta; 114 sp<MetaData> mFileMeta; 115 116 Vector<TOCEntry> mTableOfContents; 117 118 ssize_t readPage(off64_t offset, Page *page); 119 status_t findNextPage(off64_t startOffset, off64_t *pageOffset); 120 121 status_t verifyHeader( 122 MediaBuffer *buffer, uint8_t type); 123 124 void parseFileMetaData(); 125 126 status_t findPrevGranulePosition(off64_t pageOffset, uint64_t *granulePos); 127 128 void buildTableOfContents(); 129 130 MyVorbisExtractor(const MyVorbisExtractor &); 131 MyVorbisExtractor &operator=(const MyVorbisExtractor &); 132 }; 133 134 static void extractAlbumArt( 135 const sp<MetaData> &fileMeta, const void *data, size_t size); 136 137 //////////////////////////////////////////////////////////////////////////////// 138 139 OggSource::OggSource(const sp<OggExtractor> &extractor) 140 : mExtractor(extractor), 141 mStarted(false) { 142 } 143 144 OggSource::~OggSource() { 145 if (mStarted) { 146 stop(); 147 } 148 } 149 150 sp<MetaData> OggSource::getFormat() { 151 return mExtractor->mImpl->getFormat(); 152 } 153 154 status_t OggSource::start(MetaData * /* params */) { 155 if (mStarted) { 156 return INVALID_OPERATION; 157 } 158 159 mStarted = true; 160 161 return OK; 162 } 163 164 status_t OggSource::stop() { 165 mStarted = false; 166 167 return OK; 168 } 169 170 status_t OggSource::read( 171 MediaBuffer **out, const ReadOptions *options) { 172 *out = NULL; 173 174 int64_t seekTimeUs; 175 ReadOptions::SeekMode mode; 176 if (options && options->getSeekTo(&seekTimeUs, &mode)) { 177 if (mExtractor->mImpl->seekToTime(seekTimeUs) != OK) { 178 return ERROR_END_OF_STREAM; 179 } 180 } 181 182 MediaBuffer *packet; 183 status_t err = mExtractor->mImpl->readNextPacket(&packet); 184 185 if (err != OK) { 186 return err; 187 } 188 189 #if 0 190 int64_t timeUs; 191 if (packet->meta_data()->findInt64(kKeyTime, &timeUs)) { 192 ALOGI("found time = %lld us", timeUs); 193 } else { 194 ALOGI("NO time"); 195 } 196 #endif 197 198 packet->meta_data()->setInt32(kKeyIsSyncFrame, 1); 199 200 *out = packet; 201 202 return OK; 203 } 204 205 //////////////////////////////////////////////////////////////////////////////// 206 207 MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source) 208 : mSource(source), 209 mOffset(0), 210 mPrevGranulePosition(0), 211 mCurrentPageSize(0), 212 mFirstPacketInPage(true), 213 mCurrentPageSamples(0), 214 mNextLaceIndex(0), 215 mFirstDataOffset(-1) { 216 mCurrentPage.mNumSegments = 0; 217 218 vorbis_info_init(&mVi); 219 vorbis_comment_init(&mVc); 220 } 221 222 MyVorbisExtractor::~MyVorbisExtractor() { 223 vorbis_comment_clear(&mVc); 224 vorbis_info_clear(&mVi); 225 } 226 227 sp<MetaData> MyVorbisExtractor::getFormat() const { 228 return mMeta; 229 } 230 231 status_t MyVorbisExtractor::findNextPage( 232 off64_t startOffset, off64_t *pageOffset) { 233 *pageOffset = startOffset; 234 235 for (;;) { 236 char signature[4]; 237 ssize_t n = mSource->readAt(*pageOffset, &signature, 4); 238 239 if (n < 4) { 240 *pageOffset = 0; 241 242 return (n < 0) ? n : (status_t)ERROR_END_OF_STREAM; 243 } 244 245 if (!memcmp(signature, "OggS", 4)) { 246 if (*pageOffset > startOffset) { 247 ALOGV("skipped %lld bytes of junk to reach next frame", 248 *pageOffset - startOffset); 249 } 250 251 return OK; 252 } 253 254 ++*pageOffset; 255 } 256 } 257 258 // Given the offset of the "current" page, find the page immediately preceding 259 // it (if any) and return its granule position. 260 // To do this we back up from the "current" page's offset until we find any 261 // page preceding it and then scan forward to just before the current page. 262 status_t MyVorbisExtractor::findPrevGranulePosition( 263 off64_t pageOffset, uint64_t *granulePos) { 264 *granulePos = 0; 265 266 off64_t prevPageOffset = 0; 267 off64_t prevGuess = pageOffset; 268 for (;;) { 269 if (prevGuess >= 5000) { 270 prevGuess -= 5000; 271 } else { 272 prevGuess = 0; 273 } 274 275 ALOGV("backing up %lld bytes", pageOffset - prevGuess); 276 277 status_t err = findNextPage(prevGuess, &prevPageOffset); 278 if (err != OK) { 279 return err; 280 } 281 282 if (prevPageOffset < pageOffset || prevGuess == 0) { 283 break; 284 } 285 } 286 287 if (prevPageOffset == pageOffset) { 288 // We did not find a page preceding this one. 289 return UNKNOWN_ERROR; 290 } 291 292 ALOGV("prevPageOffset at %lld, pageOffset at %lld", 293 prevPageOffset, pageOffset); 294 295 for (;;) { 296 Page prevPage; 297 ssize_t n = readPage(prevPageOffset, &prevPage); 298 299 if (n <= 0) { 300 return (status_t)n; 301 } 302 303 prevPageOffset += n; 304 305 if (prevPageOffset == pageOffset) { 306 *granulePos = prevPage.mGranulePosition; 307 return OK; 308 } 309 } 310 } 311 312 status_t MyVorbisExtractor::seekToTime(int64_t timeUs) { 313 if (mTableOfContents.isEmpty()) { 314 // Perform approximate seeking based on avg. bitrate. 315 316 off64_t pos = timeUs * approxBitrate() / 8000000ll; 317 318 ALOGV("seeking to offset %lld", pos); 319 return seekToOffset(pos); 320 } 321 322 size_t left = 0; 323 size_t right_plus_one = mTableOfContents.size(); 324 while (left < right_plus_one) { 325 size_t center = left + (right_plus_one - left) / 2; 326 327 const TOCEntry &entry = mTableOfContents.itemAt(center); 328 329 if (timeUs < entry.mTimeUs) { 330 right_plus_one = center; 331 } else if (timeUs > entry.mTimeUs) { 332 left = center + 1; 333 } else { 334 left = center; 335 break; 336 } 337 } 338 339 if (left == mTableOfContents.size()) { 340 --left; 341 } 342 343 const TOCEntry &entry = mTableOfContents.itemAt(left); 344 345 ALOGV("seeking to entry %zu / %zu at offset %lld", 346 left, mTableOfContents.size(), entry.mPageOffset); 347 348 return seekToOffset(entry.mPageOffset); 349 } 350 351 status_t MyVorbisExtractor::seekToOffset(off64_t offset) { 352 if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) { 353 // Once we know where the actual audio data starts (past the headers) 354 // don't ever seek to anywhere before that. 355 offset = mFirstDataOffset; 356 } 357 358 off64_t pageOffset; 359 status_t err = findNextPage(offset, &pageOffset); 360 361 if (err != OK) { 362 return err; 363 } 364 365 // We found the page we wanted to seek to, but we'll also need 366 // the page preceding it to determine how many valid samples are on 367 // this page. 368 findPrevGranulePosition(pageOffset, &mPrevGranulePosition); 369 370 mOffset = pageOffset; 371 372 mCurrentPageSize = 0; 373 mFirstPacketInPage = true; 374 mCurrentPageSamples = 0; 375 mCurrentPage.mNumSegments = 0; 376 mNextLaceIndex = 0; 377 378 // XXX what if new page continues packet from last??? 379 380 return OK; 381 } 382 383 ssize_t MyVorbisExtractor::readPage(off64_t offset, Page *page) { 384 uint8_t header[27]; 385 ssize_t n; 386 if ((n = mSource->readAt(offset, header, sizeof(header))) 387 < (ssize_t)sizeof(header)) { 388 ALOGV("failed to read %zu bytes at offset 0x%016llx, got %zd bytes", 389 sizeof(header), offset, n); 390 391 if (n < 0) { 392 return n; 393 } else if (n == 0) { 394 return ERROR_END_OF_STREAM; 395 } else { 396 return ERROR_IO; 397 } 398 } 399 400 if (memcmp(header, "OggS", 4)) { 401 return ERROR_MALFORMED; 402 } 403 404 if (header[4] != 0) { 405 // Wrong version. 406 407 return ERROR_UNSUPPORTED; 408 } 409 410 page->mFlags = header[5]; 411 412 if (page->mFlags & ~7) { 413 // Only bits 0-2 are defined in version 0. 414 return ERROR_MALFORMED; 415 } 416 417 page->mGranulePosition = U64LE_AT(&header[6]); 418 419 #if 0 420 printf("granulePosition = %llu (0x%llx)\n", 421 page->mGranulePosition, page->mGranulePosition); 422 #endif 423 424 page->mSerialNo = U32LE_AT(&header[14]); 425 page->mPageNo = U32LE_AT(&header[18]); 426 427 page->mNumSegments = header[26]; 428 if (mSource->readAt( 429 offset + sizeof(header), page->mLace, page->mNumSegments) 430 < (ssize_t)page->mNumSegments) { 431 return ERROR_IO; 432 } 433 434 size_t totalSize = 0;; 435 for (size_t i = 0; i < page->mNumSegments; ++i) { 436 totalSize += page->mLace[i]; 437 } 438 439 #if 0 440 String8 tmp; 441 for (size_t i = 0; i < page->mNumSegments; ++i) { 442 char x[32]; 443 sprintf(x, "%s%u", i > 0 ? ", " : "", (unsigned)page->mLace[i]); 444 445 tmp.append(x); 446 } 447 448 ALOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string()); 449 #endif 450 451 return sizeof(header) + page->mNumSegments + totalSize; 452 } 453 454 status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { 455 *out = NULL; 456 457 MediaBuffer *buffer = NULL; 458 int64_t timeUs = -1; 459 460 for (;;) { 461 size_t i; 462 size_t packetSize = 0; 463 bool gotFullPacket = false; 464 for (i = mNextLaceIndex; i < mCurrentPage.mNumSegments; ++i) { 465 uint8_t lace = mCurrentPage.mLace[i]; 466 467 packetSize += lace; 468 469 if (lace < 255) { 470 gotFullPacket = true; 471 ++i; 472 break; 473 } 474 } 475 476 if (mNextLaceIndex < mCurrentPage.mNumSegments) { 477 off64_t dataOffset = mOffset + 27 + mCurrentPage.mNumSegments; 478 for (size_t j = 0; j < mNextLaceIndex; ++j) { 479 dataOffset += mCurrentPage.mLace[j]; 480 } 481 482 size_t fullSize = packetSize; 483 if (buffer != NULL) { 484 fullSize += buffer->range_length(); 485 } 486 MediaBuffer *tmp = new MediaBuffer(fullSize); 487 if (buffer != NULL) { 488 memcpy(tmp->data(), buffer->data(), buffer->range_length()); 489 tmp->set_range(0, buffer->range_length()); 490 buffer->release(); 491 } else { 492 // XXX Not only is this not technically the correct time for 493 // this packet, we also stamp every packet in this page 494 // with the same time. This needs fixing later. 495 496 if (mVi.rate) { 497 // Rate may not have been initialized yet if we're currently 498 // reading the configuration packets... 499 // Fortunately, the timestamp doesn't matter for those. 500 timeUs = mCurrentPage.mGranulePosition * 1000000ll / mVi.rate; 501 } 502 tmp->set_range(0, 0); 503 } 504 buffer = tmp; 505 506 ssize_t n = mSource->readAt( 507 dataOffset, 508 (uint8_t *)buffer->data() + buffer->range_length(), 509 packetSize); 510 511 if (n < (ssize_t)packetSize) { 512 ALOGV("failed to read %zu bytes at 0x%016llx, got %zd bytes", 513 packetSize, dataOffset, n); 514 return ERROR_IO; 515 } 516 517 buffer->set_range(0, fullSize); 518 519 mNextLaceIndex = i; 520 521 if (gotFullPacket) { 522 // We've just read the entire packet. 523 524 if (timeUs >= 0) { 525 buffer->meta_data()->setInt64(kKeyTime, timeUs); 526 } 527 528 if (mFirstPacketInPage) { 529 buffer->meta_data()->setInt32( 530 kKeyValidSamples, mCurrentPageSamples); 531 mFirstPacketInPage = false; 532 } 533 534 *out = buffer; 535 536 return OK; 537 } 538 539 // fall through, the buffer now contains the start of the packet. 540 } 541 542 CHECK_EQ(mNextLaceIndex, mCurrentPage.mNumSegments); 543 544 mOffset += mCurrentPageSize; 545 ssize_t n = readPage(mOffset, &mCurrentPage); 546 547 if (n <= 0) { 548 if (buffer) { 549 buffer->release(); 550 buffer = NULL; 551 } 552 553 ALOGV("readPage returned %zd", n); 554 555 return n < 0 ? n : (status_t)ERROR_END_OF_STREAM; 556 } 557 558 mCurrentPageSamples = 559 mCurrentPage.mGranulePosition - mPrevGranulePosition; 560 mFirstPacketInPage = true; 561 562 mPrevGranulePosition = mCurrentPage.mGranulePosition; 563 564 mCurrentPageSize = n; 565 mNextLaceIndex = 0; 566 567 if (buffer != NULL) { 568 if ((mCurrentPage.mFlags & 1) == 0) { 569 // This page does not continue the packet, i.e. the packet 570 // is already complete. 571 572 if (timeUs >= 0) { 573 buffer->meta_data()->setInt64(kKeyTime, timeUs); 574 } 575 576 buffer->meta_data()->setInt32( 577 kKeyValidSamples, mCurrentPageSamples); 578 mFirstPacketInPage = false; 579 580 *out = buffer; 581 582 return OK; 583 } 584 } 585 } 586 } 587 588 status_t MyVorbisExtractor::init() { 589 mMeta = new MetaData; 590 mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS); 591 592 MediaBuffer *packet; 593 status_t err; 594 if ((err = readNextPacket(&packet)) != OK) { 595 return err; 596 } 597 ALOGV("read packet of size %zu\n", packet->range_length()); 598 err = verifyHeader(packet, 1); 599 packet->release(); 600 packet = NULL; 601 if (err != OK) { 602 return err; 603 } 604 605 if ((err = readNextPacket(&packet)) != OK) { 606 return err; 607 } 608 ALOGV("read packet of size %zu\n", packet->range_length()); 609 err = verifyHeader(packet, 3); 610 packet->release(); 611 packet = NULL; 612 if (err != OK) { 613 return err; 614 } 615 616 if ((err = readNextPacket(&packet)) != OK) { 617 return err; 618 } 619 ALOGV("read packet of size %zu\n", packet->range_length()); 620 err = verifyHeader(packet, 5); 621 packet->release(); 622 packet = NULL; 623 if (err != OK) { 624 return err; 625 } 626 627 mFirstDataOffset = mOffset + mCurrentPageSize; 628 629 off64_t size; 630 uint64_t lastGranulePosition; 631 if (!(mSource->flags() & DataSource::kIsCachingDataSource) 632 && mSource->getSize(&size) == OK 633 && findPrevGranulePosition(size, &lastGranulePosition) == OK) { 634 // Let's assume it's cheap to seek to the end. 635 // The granule position of the final page in the stream will 636 // give us the exact duration of the content, something that 637 // we can only approximate using avg. bitrate if seeking to 638 // the end is too expensive or impossible (live streaming). 639 640 int64_t durationUs = lastGranulePosition * 1000000ll / mVi.rate; 641 642 mMeta->setInt64(kKeyDuration, durationUs); 643 644 buildTableOfContents(); 645 } 646 647 return OK; 648 } 649 650 void MyVorbisExtractor::buildTableOfContents() { 651 off64_t offset = mFirstDataOffset; 652 Page page; 653 ssize_t pageSize; 654 while ((pageSize = readPage(offset, &page)) > 0) { 655 mTableOfContents.push(); 656 657 TOCEntry &entry = 658 mTableOfContents.editItemAt(mTableOfContents.size() - 1); 659 660 entry.mPageOffset = offset; 661 entry.mTimeUs = page.mGranulePosition * 1000000ll / mVi.rate; 662 663 offset += (size_t)pageSize; 664 } 665 666 // Limit the maximum amount of RAM we spend on the table of contents, 667 // if necessary thin out the table evenly to trim it down to maximum 668 // size. 669 670 static const size_t kMaxTOCSize = 8192; 671 static const size_t kMaxNumTOCEntries = kMaxTOCSize / sizeof(TOCEntry); 672 673 size_t numerator = mTableOfContents.size(); 674 675 if (numerator > kMaxNumTOCEntries) { 676 size_t denom = numerator - kMaxNumTOCEntries; 677 678 size_t accum = 0; 679 for (ssize_t i = mTableOfContents.size() - 1; i >= 0; --i) { 680 accum += denom; 681 if (accum >= numerator) { 682 mTableOfContents.removeAt(i); 683 accum -= numerator; 684 } 685 } 686 } 687 } 688 689 status_t MyVorbisExtractor::verifyHeader( 690 MediaBuffer *buffer, uint8_t type) { 691 const uint8_t *data = 692 (const uint8_t *)buffer->data() + buffer->range_offset(); 693 694 size_t size = buffer->range_length(); 695 696 if (size < 7 || data[0] != type || memcmp(&data[1], "vorbis", 6)) { 697 return ERROR_MALFORMED; 698 } 699 700 ogg_buffer buf; 701 buf.data = (uint8_t *)data; 702 buf.size = size; 703 buf.refcount = 1; 704 buf.ptr.owner = NULL; 705 706 ogg_reference ref; 707 ref.buffer = &buf; 708 ref.begin = 0; 709 ref.length = size; 710 ref.next = NULL; 711 712 oggpack_buffer bits; 713 oggpack_readinit(&bits, &ref); 714 715 CHECK_EQ(oggpack_read(&bits, 8), type); 716 for (size_t i = 0; i < 6; ++i) { 717 oggpack_read(&bits, 8); // skip 'vorbis' 718 } 719 720 switch (type) { 721 case 1: 722 { 723 CHECK_EQ(0, _vorbis_unpack_info(&mVi, &bits)); 724 725 mMeta->setData(kKeyVorbisInfo, 0, data, size); 726 mMeta->setInt32(kKeySampleRate, mVi.rate); 727 mMeta->setInt32(kKeyChannelCount, mVi.channels); 728 729 ALOGV("lower-bitrate = %ld", mVi.bitrate_lower); 730 ALOGV("upper-bitrate = %ld", mVi.bitrate_upper); 731 ALOGV("nominal-bitrate = %ld", mVi.bitrate_nominal); 732 ALOGV("window-bitrate = %ld", mVi.bitrate_window); 733 734 off64_t size; 735 if (mSource->getSize(&size) == OK) { 736 uint64_t bps = approxBitrate(); 737 if (bps != 0) { 738 mMeta->setInt64(kKeyDuration, size * 8000000ll / bps); 739 } 740 } 741 break; 742 } 743 744 case 3: 745 { 746 if (0 != _vorbis_unpack_comment(&mVc, &bits)) { 747 return ERROR_MALFORMED; 748 } 749 750 parseFileMetaData(); 751 break; 752 } 753 754 case 5: 755 { 756 if (0 != _vorbis_unpack_books(&mVi, &bits)) { 757 return ERROR_MALFORMED; 758 } 759 760 mMeta->setData(kKeyVorbisBooks, 0, data, size); 761 break; 762 } 763 } 764 765 return OK; 766 } 767 768 uint64_t MyVorbisExtractor::approxBitrate() { 769 if (mVi.bitrate_nominal != 0) { 770 return mVi.bitrate_nominal; 771 } 772 773 return (mVi.bitrate_lower + mVi.bitrate_upper) / 2; 774 } 775 776 void MyVorbisExtractor::parseFileMetaData() { 777 mFileMeta = new MetaData; 778 mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG); 779 780 for (int i = 0; i < mVc.comments; ++i) { 781 const char *comment = mVc.user_comments[i]; 782 size_t commentLength = mVc.comment_lengths[i]; 783 parseVorbisComment(mFileMeta, comment, commentLength); 784 //ALOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]); 785 } 786 } 787 788 void parseVorbisComment( 789 const sp<MetaData> &fileMeta, const char *comment, size_t commentLength) 790 { 791 struct { 792 const char *const mTag; 793 uint32_t mKey; 794 } kMap[] = { 795 { "TITLE", kKeyTitle }, 796 { "ARTIST", kKeyArtist }, 797 { "ALBUMARTIST", kKeyAlbumArtist }, 798 { "ALBUM ARTIST", kKeyAlbumArtist }, 799 { "COMPILATION", kKeyCompilation }, 800 { "ALBUM", kKeyAlbum }, 801 { "COMPOSER", kKeyComposer }, 802 { "GENRE", kKeyGenre }, 803 { "AUTHOR", kKeyAuthor }, 804 { "TRACKNUMBER", kKeyCDTrackNumber }, 805 { "DISCNUMBER", kKeyDiscNumber }, 806 { "DATE", kKeyDate }, 807 { "LYRICIST", kKeyWriter }, 808 { "METADATA_BLOCK_PICTURE", kKeyAlbumArt }, 809 { "ANDROID_LOOP", kKeyAutoLoop }, 810 }; 811 812 for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) { 813 size_t tagLen = strlen(kMap[j].mTag); 814 if (!strncasecmp(kMap[j].mTag, comment, tagLen) 815 && comment[tagLen] == '=') { 816 if (kMap[j].mKey == kKeyAlbumArt) { 817 extractAlbumArt( 818 fileMeta, 819 &comment[tagLen + 1], 820 commentLength - tagLen - 1); 821 } else if (kMap[j].mKey == kKeyAutoLoop) { 822 if (!strcasecmp(&comment[tagLen + 1], "true")) { 823 fileMeta->setInt32(kKeyAutoLoop, true); 824 } 825 } else { 826 fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]); 827 } 828 } 829 } 830 831 } 832 833 // The returned buffer should be free()d. 834 static uint8_t *DecodeBase64(const char *s, size_t size, size_t *outSize) { 835 *outSize = 0; 836 837 if ((size % 4) != 0) { 838 return NULL; 839 } 840 841 size_t n = size; 842 size_t padding = 0; 843 if (n >= 1 && s[n - 1] == '=') { 844 padding = 1; 845 846 if (n >= 2 && s[n - 2] == '=') { 847 padding = 2; 848 } 849 } 850 851 size_t outLen = 3 * size / 4 - padding; 852 853 *outSize = outLen; 854 855 void *buffer = malloc(outLen); 856 857 uint8_t *out = (uint8_t *)buffer; 858 size_t j = 0; 859 uint32_t accum = 0; 860 for (size_t i = 0; i < n; ++i) { 861 char c = s[i]; 862 unsigned value; 863 if (c >= 'A' && c <= 'Z') { 864 value = c - 'A'; 865 } else if (c >= 'a' && c <= 'z') { 866 value = 26 + c - 'a'; 867 } else if (c >= '0' && c <= '9') { 868 value = 52 + c - '0'; 869 } else if (c == '+') { 870 value = 62; 871 } else if (c == '/') { 872 value = 63; 873 } else if (c != '=') { 874 return NULL; 875 } else { 876 if (i < n - padding) { 877 return NULL; 878 } 879 880 value = 0; 881 } 882 883 accum = (accum << 6) | value; 884 885 if (((i + 1) % 4) == 0) { 886 out[j++] = (accum >> 16); 887 888 if (j < outLen) { out[j++] = (accum >> 8) & 0xff; } 889 if (j < outLen) { out[j++] = accum & 0xff; } 890 891 accum = 0; 892 } 893 } 894 895 return (uint8_t *)buffer; 896 } 897 898 static void extractAlbumArt( 899 const sp<MetaData> &fileMeta, const void *data, size_t size) { 900 ALOGV("extractAlbumArt from '%s'", (const char *)data); 901 902 size_t flacSize; 903 uint8_t *flac = DecodeBase64((const char *)data, size, &flacSize); 904 905 if (flac == NULL) { 906 ALOGE("malformed base64 encoded data."); 907 return; 908 } 909 910 ALOGV("got flac of size %zu", flacSize); 911 912 uint32_t picType; 913 uint32_t typeLen; 914 uint32_t descLen; 915 uint32_t dataLen; 916 char type[128]; 917 918 if (flacSize < 8) { 919 goto exit; 920 } 921 922 picType = U32_AT(flac); 923 924 if (picType != 3) { 925 // This is not a front cover. 926 goto exit; 927 } 928 929 typeLen = U32_AT(&flac[4]); 930 if (typeLen + 1 > sizeof(type)) { 931 goto exit; 932 } 933 934 if (flacSize < 8 + typeLen) { 935 goto exit; 936 } 937 938 memcpy(type, &flac[8], typeLen); 939 type[typeLen] = '\0'; 940 941 ALOGV("picType = %d, type = '%s'", picType, type); 942 943 if (!strcmp(type, "-->")) { 944 // This is not inline cover art, but an external url instead. 945 goto exit; 946 } 947 948 descLen = U32_AT(&flac[8 + typeLen]); 949 950 if (flacSize < 32 + typeLen + descLen) { 951 goto exit; 952 } 953 954 dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]); 955 956 if (flacSize < 32 + typeLen + descLen + dataLen) { 957 goto exit; 958 } 959 960 ALOGV("got image data, %zu trailing bytes", 961 flacSize - 32 - typeLen - descLen - dataLen); 962 963 fileMeta->setData( 964 kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen); 965 966 fileMeta->setCString(kKeyAlbumArtMIME, type); 967 968 exit: 969 free(flac); 970 flac = NULL; 971 } 972 973 //////////////////////////////////////////////////////////////////////////////// 974 975 OggExtractor::OggExtractor(const sp<DataSource> &source) 976 : mDataSource(source), 977 mInitCheck(NO_INIT), 978 mImpl(NULL) { 979 mImpl = new MyVorbisExtractor(mDataSource); 980 mInitCheck = mImpl->seekToOffset(0); 981 982 if (mInitCheck == OK) { 983 mInitCheck = mImpl->init(); 984 } 985 } 986 987 OggExtractor::~OggExtractor() { 988 delete mImpl; 989 mImpl = NULL; 990 } 991 992 size_t OggExtractor::countTracks() { 993 return mInitCheck != OK ? 0 : 1; 994 } 995 996 sp<MediaSource> OggExtractor::getTrack(size_t index) { 997 if (index >= 1) { 998 return NULL; 999 } 1000 1001 return new OggSource(this); 1002 } 1003 1004 sp<MetaData> OggExtractor::getTrackMetaData( 1005 size_t index, uint32_t /* flags */) { 1006 if (index >= 1) { 1007 return NULL; 1008 } 1009 1010 return mImpl->getFormat(); 1011 } 1012 1013 sp<MetaData> OggExtractor::getMetaData() { 1014 return mImpl->getFileMetaData(); 1015 } 1016 1017 bool SniffOgg( 1018 const sp<DataSource> &source, String8 *mimeType, float *confidence, 1019 sp<AMessage> *) { 1020 char tmp[4]; 1021 if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) { 1022 return false; 1023 } 1024 1025 mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_OGG); 1026 *confidence = 0.2f; 1027 1028 return true; 1029 } 1030 1031 } // namespace android 1032