diff options
author | Andreas Huber <andih@google.com> | 2010-05-25 11:20:29 -0700 |
---|---|---|
committer | Andreas Huber <andih@google.com> | 2010-05-25 11:20:29 -0700 |
commit | c6c62e12c930b137e62c16931cfe340bc93aa8f4 (patch) | |
tree | 8be9a7ed608ecd2cb572f22f81bf61eb257a549f | |
parent | cf3fa85cdb6881827a379632c905f86fab2edc34 (diff) | |
download | frameworks_base-c6c62e12c930b137e62c16931cfe340bc93aa8f4.zip frameworks_base-c6c62e12c930b137e62c16931cfe340bc93aa8f4.tar.gz frameworks_base-c6c62e12c930b137e62c16931cfe340bc93aa8f4.tar.bz2 |
Support for ogg(vorbis) metadata in stagefright including album art.
Change-Id: I0c8c0136cebe2d2d97caabb7bc0c65be86329dbb
related-to-bug: 2713414
-rw-r--r-- | media/libstagefright/OggExtractor.cpp | 215 | ||||
-rw-r--r-- | media/libstagefright/StagefrightMediaScanner.cpp | 50 |
2 files changed, 201 insertions, 64 deletions
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index d0d1b14..bd16db9 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -78,6 +78,8 @@ struct MyVorbisExtractor { void init(); + sp<MetaData> getFileMetaData() { return mFileMeta; } + private: struct Page { uint64_t mGranulePosition; @@ -100,6 +102,7 @@ private: vorbis_comment mVc; sp<MetaData> mMeta; + sp<MetaData> mFileMeta; ssize_t readPage(off_t offset, Page *page); status_t findNextPage(off_t startOffset, off_t *pageOffset); @@ -107,6 +110,9 @@ private: void verifyHeader( MediaBuffer *buffer, uint8_t type); + void parseFileMetaData(); + void extractAlbumArt(const void *data, size_t size); + MyVorbisExtractor(const MyVorbisExtractor &); MyVorbisExtractor &operator=(const MyVorbisExtractor &); }; @@ -188,9 +194,14 @@ MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source) mNextLaceIndex(0), mFirstDataOffset(-1) { mCurrentPage.mNumSegments = 0; + + vorbis_info_init(&mVi); + vorbis_comment_init(&mVc); } MyVorbisExtractor::~MyVorbisExtractor() { + vorbis_comment_clear(&mVc); + vorbis_info_clear(&mVi); } sp<MetaData> MyVorbisExtractor::getFormat() const { @@ -305,7 +316,7 @@ ssize_t MyVorbisExtractor::readPage(off_t offset, Page *page) { tmp.append(x); } - LOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string()); + // LOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string()); return sizeof(header) + page->mNumSegments + totalSize; } @@ -422,10 +433,6 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { } void MyVorbisExtractor::init() { - vorbis_info_init(&mVi); - - vorbis_comment mVc; - mMeta = new MetaData; mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS); @@ -509,6 +516,8 @@ void MyVorbisExtractor::verifyHeader( case 3: { CHECK_EQ(0, _vorbis_unpack_comment(&mVc, &bits)); + + parseFileMetaData(); break; } @@ -530,6 +539,192 @@ uint64_t MyVorbisExtractor::approxBitrate() { return (mVi.bitrate_lower + mVi.bitrate_upper) / 2; } +void MyVorbisExtractor::parseFileMetaData() { + mFileMeta = new MetaData; + mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG); + + struct { + const char *const mTag; + uint32_t mKey; + } kMap[] = { + { "TITLE", kKeyTitle }, + { "ARTIST", kKeyArtist }, + { "ALBUM", kKeyAlbum }, + { "COMPOSER", kKeyComposer }, + { "GENRE", kKeyGenre }, + { "AUTHOR", kKeyAuthor }, + { "TRACKNUMBER", kKeyCDTrackNumber }, + { "DISCNUMBER", kKeyDiscNumber }, + { "DATE", kKeyDate }, + { "LYRICIST", kKeyWriter }, + { "METADATA_BLOCK_PICTURE", kKeyAlbumArt }, + }; + + for (int i = 0; i < mVc.comments; ++i) { + const char *comment = mVc.user_comments[i]; + + for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) { + size_t tagLen = strlen(kMap[j].mTag); + if (!strncasecmp(kMap[j].mTag, comment, tagLen) + && comment[tagLen] == '=') { + if (kMap[j].mKey == kKeyAlbumArt) { + extractAlbumArt( + &comment[tagLen + 1], + mVc.comment_lengths[i] - tagLen - 1); + } else { + mFileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]); + } + } + } + + } + +#if 0 + for (int i = 0; i < mVc.comments; ++i) { + LOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]); + } +#endif +} + +// The returned buffer should be free()d. +static uint8_t *DecodeBase64(const char *s, size_t size, size_t *outSize) { + *outSize = 0; + + if ((size % 4) != 0) { + return NULL; + } + + size_t n = size; + size_t padding = 0; + if (n >= 1 && s[n - 1] == '=') { + padding = 1; + + if (n >= 2 && s[n - 2] == '=') { + padding = 2; + } + } + + size_t outLen = 3 * size / 4 - padding; + + *outSize = outLen; + + void *buffer = malloc(outLen); + + uint8_t *out = (uint8_t *)buffer; + size_t j = 0; + uint32_t accum = 0; + for (size_t i = 0; i < n; ++i) { + char c = s[i]; + unsigned value; + if (c >= 'A' && c <= 'Z') { + value = c - 'A'; + } else if (c >= 'a' && c <= 'z') { + value = 26 + c - 'a'; + } else if (c >= '0' && c <= '9') { + value = 52 + c - '0'; + } else if (c == '+') { + value = 62; + } else if (c == '/') { + value = 63; + } else if (c != '=') { + return NULL; + } else { + if (i < n - padding) { + return NULL; + } + + value = 0; + } + + accum = (accum << 6) | value; + + if (((i + 1) % 4) == 0) { + out[j++] = (accum >> 16); + + if (j < outLen) { out[j++] = (accum >> 8) & 0xff; } + if (j < outLen) { out[j++] = accum & 0xff; } + + accum = 0; + } + } + + return (uint8_t *)buffer; +} + +void MyVorbisExtractor::extractAlbumArt(const void *data, size_t size) { + LOGV("extractAlbumArt from '%s'", (const char *)data); + + size_t flacSize; + uint8_t *flac = DecodeBase64((const char *)data, size, &flacSize); + + if (flac == NULL) { + LOGE("malformed base64 encoded data."); + return; + } + + LOGV("got flac of size %d", flacSize); + + uint32_t picType; + uint32_t typeLen; + uint32_t descLen; + uint32_t dataLen; + char type[128]; + + if (flacSize < 8) { + goto exit; + } + + picType = U32_AT(flac); + + if (picType != 3) { + // This is not a front cover. + goto exit; + } + + typeLen = U32_AT(&flac[4]); + if (typeLen + 1 > sizeof(type)) { + goto exit; + } + + if (flacSize < 8 + typeLen) { + goto exit; + } + + memcpy(type, &flac[8], typeLen); + type[typeLen] = '\0'; + + LOGV("picType = %d, type = '%s'", picType, type); + + if (!strcmp(type, "-->")) { + // This is not inline cover art, but an external url instead. + goto exit; + } + + descLen = U32_AT(&flac[8 + typeLen]); + + if (flacSize < 32 + typeLen + descLen) { + goto exit; + } + + dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]); + + if (flacSize < 32 + typeLen + descLen + dataLen) { + goto exit; + } + + LOGV("got image data, %d trailing bytes", + flacSize - 32 - typeLen - descLen - dataLen); + + mFileMeta->setData( + kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen); + + mFileMeta->setCString(kKeyAlbumArtMIME, type); + +exit: + free(flac); + flac = NULL; +} + //////////////////////////////////////////////////////////////////////////////// OggExtractor::OggExtractor(const sp<DataSource> &source) @@ -570,15 +765,7 @@ sp<MetaData> OggExtractor::getTrackMetaData( } sp<MetaData> OggExtractor::getMetaData() { - sp<MetaData> meta = new MetaData; - - if (mInitCheck != OK) { - return meta; - } - - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG); - - return meta; + return mImpl->getFileMetaData(); } bool SniffOgg( diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index ab17b04..2829638 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -26,10 +26,6 @@ // Sonivox includes #include <libsonivox/eas.h> -// Ogg Vorbis includes -#include <Tremolo/ivorbiscodec.h> -#include <Tremolo/ivorbisfile.h> - namespace android { StagefrightMediaScanner::StagefrightMediaScanner() @@ -103,48 +99,6 @@ static status_t HandleMIDI( return OK; } -static status_t HandleOGG( - const char *filename, MediaScannerClient *client) { - int duration; - - FILE *file = fopen(filename,"r"); - if (!file) - return UNKNOWN_ERROR; - - OggVorbis_File vf; - if (ov_open(file, &vf, NULL, 0) < 0) { - return UNKNOWN_ERROR; - } - - char **ptr=ov_comment(&vf,-1)->user_comments; - while(*ptr){ - char *val = strstr(*ptr, "="); - if (val) { - int keylen = val++ - *ptr; - char key[keylen + 1]; - strncpy(key, *ptr, keylen); - key[keylen] = 0; - if (!client->addStringTag(key, val)) goto failure; - } - ++ptr; - } - - // Duration - duration = ov_time_total(&vf, -1); - if (duration > 0) { - char buffer[20]; - sprintf(buffer, "%d", duration); - if (!client->addStringTag("duration", buffer)) goto failure; - } - - ov_clear(&vf); // this also closes the FILE - return OK; - -failure: - ov_clear(&vf); // this also closes the FILE - return UNKNOWN_ERROR; -} - status_t StagefrightMediaScanner::processFile( const char *path, const char *mimeType, MediaScannerClient &client) { @@ -176,10 +130,6 @@ status_t StagefrightMediaScanner::processFile( return HandleMIDI(path, &client); } - if (!strcasecmp(extension, ".ogg")) { - return HandleOGG(path, &client); - } - if (mRetriever->setDataSource(path) == OK && mRetriever->setMode( METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) { |