summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/OggExtractor.cpp
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2010-05-25 11:20:29 -0700
committerAndreas Huber <andih@google.com>2010-05-25 11:20:29 -0700
commit4595be952e3b1d6776a023e3f447c057797f1505 (patch)
tree57649e8d90a0f4b378aaa3928d20f6e855c6d7e1 /media/libstagefright/OggExtractor.cpp
parent9bca89d50d0c51d8e3f3e74d610b7d9a2dc98ed3 (diff)
downloadframeworks_av-4595be952e3b1d6776a023e3f447c057797f1505.zip
frameworks_av-4595be952e3b1d6776a023e3f447c057797f1505.tar.gz
frameworks_av-4595be952e3b1d6776a023e3f447c057797f1505.tar.bz2
Support for ogg(vorbis) metadata in stagefright including album art.
Change-Id: I0c8c0136cebe2d2d97caabb7bc0c65be86329dbb related-to-bug: 2713414
Diffstat (limited to 'media/libstagefright/OggExtractor.cpp')
-rw-r--r--media/libstagefright/OggExtractor.cpp215
1 files changed, 201 insertions, 14 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(