Squashed commit of the following:

commit f81bb1dac5ef107bb0d7d5d756fb1ffa532ba2cc
Author: Andreas Huber <andih@google.com>
Date:   Mon Jan 11 14:55:56 2010 -0800

    Support for duration metadata, midi and ogg-vorbis files (in mediascanner)

commit 0b1385a0dc156ce27985a1ff757c4c142fd7ec39
Author: Andreas Huber <andih@google.com>
Date:   Mon Jan 11 14:20:45 2010 -0800

    Refactor meta data logic. Container specific metadata is now also returned by the MediaExtractor.

commit f9818dfac39c96e5fefe8c8295e60580692d5990
Author: Andreas Huber <andih@google.com>
Date:   Fri Jan 8 14:26:09 2010 -0800

    A first pass at supporting metadata through ID3 tags.

commit 476e9e253633336ab790f943e2d6c0cd8991d76a
Author: Andreas Huber <andih@google.com>
Date:   Thu Jan 7 15:48:44 2010 -0800

    Initial checkin of ID3 (V2.2 and V2.3) parser for use in stagefright.

related-to-bug: 2295456
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index b843cd9..f88666a 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -31,6 +31,8 @@
 
 class DataSource : public RefBase {
 public:
+    static sp<DataSource> CreateFromURI(const char *uri);
+
     DataSource() {}
 
     virtual status_t initCheck() const = 0;
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index d56d4b3..4bc996e 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -43,6 +43,10 @@
     virtual sp<MetaData> getTrackMetaData(
             size_t index, uint32_t flags = 0) = 0;
 
+    // Return container specific meta-data. The default implementation
+    // returns an empty metadata object.
+    virtual sp<MetaData> getMetaData();
+
 protected:
     MediaExtractor() {}
     virtual ~MediaExtractor() {}
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index c6ac6c2..ef30b02 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -48,6 +48,15 @@
     kKeyBufferID          = 'bfID',
     kKeyMaxInputSize      = 'inpS',
     kKeyThumbnailTime     = 'thbT',  // int64_t (usecs)
+
+    kKeyAlbum             = 'albu',  // cstring
+    kKeyArtist            = 'arti',  // cstring
+    kKeyComposer          = 'comp',  // cstring
+    kKeyGenre             = 'genr',  // cstring
+    kKeyTitle             = 'titl',  // cstring
+    kKeyYear              = 'year',  // cstring
+    kKeyAlbumArt          = 'albA',  // compressed image data
+    kKeyAlbumArtMIME      = 'alAM',  // cstring
 };
 
 enum {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 30b4506..e36e78c 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -44,14 +44,17 @@
 LOCAL_C_INCLUDES:= \
 	$(JNI_H_INCLUDE) \
         $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
-        $(TOP)/external/opencore/android
+        $(TOP)/external/opencore/android \
+        $(TOP)/external/tremor/Tremor
 
 LOCAL_SHARED_LIBRARIES := \
         libbinder         \
         libmedia          \
         libutils          \
         libcutils         \
-        libui
+        libui             \
+        libsonivox        \
+        libvorbisidec
 
 ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
 
@@ -62,7 +65,8 @@
         libstagefright_amrwbdec \
         libstagefright_avcdec \
         libstagefright_m4vh263dec \
-        libstagefright_mp3dec
+        libstagefright_mp3dec \
+        libstagefright_id3
 
 LOCAL_SHARED_LIBRARIES += \
         libstagefright_amrnb_common \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 4280683..14842c0 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -133,13 +133,14 @@
 
     if (mAudioSink.get() != NULL) {
         mAudioSink->stop();
+        mAudioSink->close();
     } else {
         mAudioTrack->stop();
 
         delete mAudioTrack;
         mAudioTrack = NULL;
     }
-    
+
     // Make sure to release any buffer we hold onto so that the
     // source is able to stop().
     if (mInputBuffer != NULL) {
@@ -150,7 +151,7 @@
     }
 
     mSource->stop();
-    
+
     mNumFramesPlayed = 0;
     mPositionTimeMediaUs = -1;
     mPositionTimeRealUs = -1;
@@ -259,7 +260,7 @@
 
         mInputBuffer->set_range(mInputBuffer->range_offset() + copy,
                                 mInputBuffer->range_length() - copy);
-                    
+
         size_done += copy;
         size_remaining -= copy;
     }
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 2a6dbc4..741e5e00 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -19,7 +19,10 @@
 #include "include/MPEG4Extractor.h"
 #include "include/WAVExtractor.h"
 
+#include <media/stagefright/CachingDataSource.h>
 #include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/HTTPDataSource.h>
 #include <media/stagefright/MediaErrors.h>
 #include <utils/String8.h>
 
@@ -91,4 +94,24 @@
     RegisterSniffer(SniffWAV);
 }
 
+// static
+sp<DataSource> DataSource::CreateFromURI(const char *uri) {
+    sp<DataSource> source;
+    if (!strncasecmp("file://", uri, 7)) {
+        source = new FileSource(uri + 7);
+    } else if (!strncasecmp("http://", uri, 7)) {
+        source = new HTTPDataSource(uri);
+        source = new CachingDataSource(source, 64 * 1024, 10);
+    } else {
+        // Assume it's a filename.
+        source = new FileSource(uri);
+    }
+
+    if (source == NULL || source->initCheck() != OK) {
+        return NULL;
+    }
+
+    return source;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index 37c2450..b6f1af2 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -58,7 +58,10 @@
     }
 
     int err = fseeko(mFile, offset + mOffset, SEEK_SET);
-    CHECK(err != -1);
+    if (err < 0) {
+        LOGE("seek to %lld failed", offset + mOffset);
+        return UNKNOWN_ERROR;
+    }
 
     return fread(data, 1, size, mFile);
 }
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index b8e76fd..5df1e00 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -20,6 +20,8 @@
 
 #include "include/MP3Extractor.h"
 
+#include "include/ID3.h"
+
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
@@ -667,7 +669,7 @@
         }
 
         // Lost sync.
-        LOGW("lost sync!\n");
+        LOGV("lost sync!\n");
 
         off_t pos = mCurrentPos;
         if (!Resync(mDataSource, mFixedHeader, &pos, NULL)) {
@@ -706,6 +708,63 @@
     return OK;
 }
 
+sp<MetaData> MP3Extractor::getMetaData() {
+    sp<MetaData> meta = new MetaData;
+
+    meta->setCString(kKeyMIMEType, "audio/mpeg");
+
+    ID3 id3(mDataSource);
+
+    if (!id3.isValid()) {
+        return meta;
+    }
+
+    struct Map {
+        int key;
+        const char *tag1;
+        const char *tag2;
+    };
+    static const Map kMap[] = {
+        { kKeyAlbum, "TALB", "TAL" },
+        { kKeyArtist, "TPE1", "TP1" },
+        { kKeyComposer, "TCOM", "TCM" },
+        { kKeyGenre, "TCON", "TCO" },
+        { kKeyTitle, "TALB", "TAL" },
+        { kKeyYear, "TYE", "TYER" },
+    };
+    static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
+
+    for (size_t i = 0; i < kNumMapEntries; ++i) {
+        ID3::Iterator *it = new ID3::Iterator(id3, kMap[i].tag1);
+        if (it->done()) {
+            delete it;
+            it = new ID3::Iterator(id3, kMap[i].tag2);
+        }
+
+        if (it->done()) {
+            delete it;
+            continue;
+        }
+
+        String8 s;
+        it->getString(&s);
+        delete it;
+
+        meta->setCString(kMap[i].key, s);
+    }
+
+    size_t dataSize;
+    String8 mime;
+    const void *data = id3.getAlbumArt(&dataSize, &mime);
+
+    if (data) {
+        meta->setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
+        meta->setCString(kKeyAlbumArtMIME, mime.string());
+    }
+
+    return meta;
+}
+
 bool SniffMP3(
         const sp<DataSource> &source, String8 *mimeType, float *confidence) {
     off_t pos = 0;
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 9d3deb7..e46f00e 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -23,16 +23,18 @@
 #include "include/MPEG4Extractor.h"
 #include "include/WAVExtractor.h"
 
-#include <media/stagefright/CachingDataSource.h>
 #include <media/stagefright/DataSource.h>
-#include <media/stagefright/FileSource.h>
-#include <media/stagefright/HTTPDataSource.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MetaData.h>
 #include <utils/String8.h>
 
 namespace android {
 
+sp<MetaData> MediaExtractor::getMetaData() {
+    return new MetaData;
+}
+
 // static
 sp<MediaExtractor> MediaExtractor::Create(
         const sp<DataSource> &source, const char *mime) {
@@ -40,7 +42,7 @@
     if (mime == NULL) {
         float confidence;
         if (!source->sniff(&tmp, &confidence)) {
-            LOGE("FAILED to autodetect media content.");
+            LOGV("FAILED to autodetect media content.");
 
             return NULL;
         }
@@ -68,16 +70,7 @@
 // static
 sp<MediaExtractor> MediaExtractor::CreateFromURI(
         const char *uri, const char *mime) {
-    sp<DataSource> source;
-    if (!strncasecmp("file://", uri, 7)) {
-        source = new FileSource(uri + 7);
-    } else if (!strncasecmp("http://", uri, 7)) {
-        source = new HTTPDataSource(uri);
-        source = new CachingDataSource(source, 64 * 1024, 10);
-    } else {
-        // Assume it's a filename.
-        source = new FileSource(uri);
-    }
+    sp<DataSource> source = DataSource::CreateFromURI(uri);
 
     if (source == NULL || source->initCheck() != OK) {
         return NULL;
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 9b41929..4815db2 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -14,10 +14,21 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "StagefrightMediaScanner"
+#include <utils/Log.h>
+
 #include <media/stagefright/StagefrightMediaScanner.h>
 
 #include "include/StagefrightMetadataRetriever.h"
 
+// Sonivox includes
+#include <libsonivox/eas.h>
+
+// Ogg Vorbis includes
+#include "ivorbiscodec.h"
+#include "ivorbisfile.h"
+
 namespace android {
 
 StagefrightMediaScanner::StagefrightMediaScanner()
@@ -26,12 +37,145 @@
 
 StagefrightMediaScanner::~StagefrightMediaScanner() {}
 
+static bool FileHasAcceptableExtension(const char *extension) {
+    static const char *kValidExtensions[] = {
+        ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
+        ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
+        ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota"
+    };
+    static const size_t kNumValidExtensions =
+        sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
+
+    for (size_t i = 0; i < kNumValidExtensions; ++i) {
+        if (!strcasecmp(extension, kValidExtensions[i])) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static status_t HandleMIDI(
+        const char *filename, MediaScannerClient *client) {
+    // get the library configuration and do sanity check
+    const S_EAS_LIB_CONFIG* pLibConfig = EAS_Config();
+    if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) {
+        LOGE("EAS library/header mismatch\n");
+        return UNKNOWN_ERROR;
+    }
+    EAS_I32 temp;
+
+    // spin up a new EAS engine
+    EAS_DATA_HANDLE easData = NULL;
+    EAS_HANDLE easHandle = NULL;
+    EAS_RESULT result = EAS_Init(&easData);
+    if (result == EAS_SUCCESS) {
+        EAS_FILE file;
+        file.path = filename;
+        file.fd = 0;
+        file.offset = 0;
+        file.length = 0;
+        result = EAS_OpenFile(easData, &file, &easHandle);
+    }
+    if (result == EAS_SUCCESS) {
+        result = EAS_Prepare(easData, easHandle);
+    }
+    if (result == EAS_SUCCESS) {
+        result = EAS_ParseMetaData(easData, easHandle, &temp);
+    }
+    if (easHandle) {
+        EAS_CloseFile(easData, easHandle);
+    }
+    if (easData) {
+        EAS_Shutdown(easData);
+    }
+
+    if (result != EAS_SUCCESS) {
+        return UNKNOWN_ERROR;
+    }
+
+    char buffer[20];
+    sprintf(buffer, "%ld", temp);
+    if (!client->addStringTag("duration", buffer)) return UNKNOWN_ERROR;
+
+    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) {
     client.setLocale(locale());
     client.beginFile();
 
+    const char *extension = strrchr(path, '.');
+
+    if (!extension) {
+        return UNKNOWN_ERROR;
+    }
+
+    if (!FileHasAcceptableExtension(extension)) {
+        client.endFile();
+
+        return UNKNOWN_ERROR;
+    }
+
+    if (!strcasecmp(extension, ".mid")
+            || !strcasecmp(extension, ".smf")
+            || !strcasecmp(extension, ".imy")
+            || !strcasecmp(extension, ".midi")
+            || !strcasecmp(extension, ".xmf")
+            || !strcasecmp(extension, ".rtttl")
+            || !strcasecmp(extension, ".rtx")
+            || !strcasecmp(extension, ".ota")) {
+        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) {
@@ -66,12 +210,27 @@
 }
 
 char *StagefrightMediaScanner::extractAlbumArt(int fd) {
-    if (mRetriever->setDataSource(fd, 0, 0) == OK
+    off_t size = lseek(fd, 0, SEEK_END);
+    if (size < 0) {
+        return NULL;
+    }
+    lseek(fd, 0, SEEK_SET);
+
+    if (mRetriever->setDataSource(fd, 0, size) == OK
             && mRetriever->setMode(
                 METADATA_MODE_FRAME_CAPTURE_ONLY) == OK) {
         MediaAlbumArt *art = mRetriever->extractAlbumArt();
 
-        // TODO: figure out what format the result should be in.
+        if (art != NULL) {
+            char *data = (char *)malloc(art->mSize + 4);
+            *(int32_t *)data = art->mSize;
+            memcpy(&data[4], art->mData, art->mSize);
+
+            delete art;
+            art = NULL;
+
+            return data;
+        }
     }
 
     return NULL;
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 128e776..be4a9d9 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -1,19 +1,18 @@
 /*
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "StagefrightMetadataRetriever"
@@ -33,7 +32,9 @@
 
 namespace android {
 
-StagefrightMetadataRetriever::StagefrightMetadataRetriever() {
+StagefrightMetadataRetriever::StagefrightMetadataRetriever()
+    : mParsedMetaData(false),
+      mAlbumArt(NULL) {
     LOGV("StagefrightMetadataRetriever()");
 
     DataSource::RegisterDefaultSniffers();
@@ -42,23 +43,66 @@
 
 StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
     LOGV("~StagefrightMetadataRetriever()");
+
+    delete mAlbumArt;
+    mAlbumArt = NULL;
+
     mClient.disconnect();
 }
 
 status_t StagefrightMetadataRetriever::setDataSource(const char *uri) {
     LOGV("setDataSource(%s)", uri);
 
-    mExtractor = MediaExtractor::CreateFromURI(uri);
+    mParsedMetaData = false;
+    mMetaData.clear();
+    delete mAlbumArt;
+    mAlbumArt = NULL;
 
-    return mExtractor.get() != NULL ? OK : UNKNOWN_ERROR;
+    mSource = DataSource::CreateFromURI(uri);
+
+    if (mSource == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    mExtractor = MediaExtractor::Create(mSource);
+
+    if (mExtractor == NULL) {
+        mSource.clear();
+
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
 }
 
+// Warning caller retains ownership of the filedescriptor! Dup it if necessary.
 status_t StagefrightMetadataRetriever::setDataSource(
         int fd, int64_t offset, int64_t length) {
+    fd = dup(fd);
+
     LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
 
-    mExtractor = MediaExtractor::Create(
-            new FileSource(fd, offset, length));
+    mParsedMetaData = false;
+    mMetaData.clear();
+    delete mAlbumArt;
+    mAlbumArt = NULL;
+
+    mSource = new FileSource(fd, offset, length);
+
+    status_t err;
+    if ((err = mSource->initCheck()) != OK) {
+        mSource.clear();
+
+        return err;
+    }
+
+    mExtractor = MediaExtractor::Create(mSource);
+
+    if (mExtractor == NULL) {
+        mSource.clear();
+
+        return UNKNOWN_ERROR;
+    }
 
     return OK;
 }
@@ -184,14 +228,98 @@
 MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
     LOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
 
+    if (mExtractor == NULL) {
+        return NULL;
+    }
+
+    if (!mParsedMetaData) {
+        parseMetaData();
+
+        mParsedMetaData = true;
+    }
+
+    if (mAlbumArt) {
+        return new MediaAlbumArt(*mAlbumArt);
+    }
+
     return NULL;
 }
 
 const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
-    LOGV("extractMetadata %d (extractor: %s)",
-         keyCode, mExtractor.get() != NULL ? "YES" : "NO");
+    LOGV("extractMetadata %d", keyCode);
 
-    return NULL;
+    if (mExtractor == NULL) {
+        return NULL;
+    }
+
+    if (!mParsedMetaData) {
+        parseMetaData();
+
+        mParsedMetaData = true;
+    }
+
+    ssize_t index = mMetaData.indexOfKey(keyCode);
+
+    if (index < 0) {
+        return NULL;
+    }
+
+    return strdup(mMetaData.valueAt(index).string());
 }
 
+void StagefrightMetadataRetriever::parseMetaData() {
+    sp<MetaData> meta = mExtractor->getMetaData();
+
+    struct Map {
+        int from;
+        int to;
+    };
+    static const Map kMap[] = {
+        { kKeyAlbum, METADATA_KEY_ALBUM },
+        { kKeyArtist, METADATA_KEY_ARTIST },
+        { kKeyComposer, METADATA_KEY_COMPOSER },
+        { kKeyGenre, METADATA_KEY_GENRE },
+        { kKeyTitle, METADATA_KEY_TITLE },
+        { kKeyYear, METADATA_KEY_YEAR },
+    };
+    static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
+
+    for (size_t i = 0; i < kNumMapEntries; ++i) {
+        const char *value;
+        if (meta->findCString(kMap[i].from, &value)) {
+            mMetaData.add(kMap[i].to, String8(value));
+        }
+    }
+
+    const void *data;
+    uint32_t type;
+    size_t dataSize;
+    if (meta->findData(kKeyAlbumArt, &type, &data, &dataSize)) {
+        mAlbumArt = new MediaAlbumArt;
+        mAlbumArt->mSize = dataSize;
+        mAlbumArt->mData = new uint8_t[dataSize];
+        memcpy(mAlbumArt->mData, data, dataSize);
+    }
+
+    // The overall duration is the duration of the longest track.
+    int64_t maxDurationUs = 0;
+    for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
+        sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
+
+        int64_t durationUs;
+        if (trackMeta->findInt64(kKeyDuration, &durationUs)) {
+            if (durationUs > maxDurationUs) {
+                maxDurationUs = durationUs;
+            }
+        }
+    }
+
+    // The duration value is a string representing the duration in ms.
+    char tmp[32];
+    sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000);
+
+    mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
+}
+
+
 }  // namespace android
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index 229e933..d874224 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -383,7 +383,7 @@
 
                 return OK;
             } else {
-                LOGE("failed to decode frame (res = %d)", res);
+                LOGV("failed to decode frame (res = %d)", res);
                 return UNKNOWN_ERROR;
             }
         }
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
new file mode 100644
index 0000000..3c47e2e
--- /dev/null
+++ b/media/libstagefright/id3/Android.mk
@@ -0,0 +1,27 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	ID3.cpp
+
+LOCAL_MODULE := libstagefright_id3
+
+include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	testid3.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libstagefright libutils libbinder
+
+LOCAL_STATIC_LIBRARIES := \
+        libstagefright_id3
+
+LOCAL_MODULE := testid3
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
new file mode 100644
index 0000000..2b3ef1a
--- /dev/null
+++ b/media/libstagefright/id3/ID3.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ID3"
+#include <utils/Log.h>
+
+#include "../include/ID3.h"
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+namespace android {
+
+ID3::ID3(const sp<DataSource> &source)
+    : mIsValid(false),
+      mData(NULL),
+      mSize(0),
+      mFirstFrameOffset(0),
+      mVersion(ID3_UNKNOWN) {
+    mIsValid = parse(source);
+}
+
+ID3::~ID3() {
+    if (mData) {
+        free(mData);
+        mData = NULL;
+    }
+}
+
+bool ID3::isValid() const {
+    return mIsValid;
+}
+
+ID3::Version ID3::version() const {
+    return mVersion;
+}
+
+bool ID3::parse(const sp<DataSource> &source) {
+    struct id3_header {
+        char id[3];
+        uint8_t version_major;
+        uint8_t version_minor;
+        uint8_t flags;
+        uint8_t enc_size[4];
+    };
+
+    id3_header header;
+    if (source->readAt(
+                0, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
+        return false;
+    }
+
+    if (memcmp(header.id, "ID3", 3)) {
+        return false;
+    }
+
+    if (header.version_major == 0xff || header.version_minor == 0xff) {
+        return false;
+    }
+
+    if (header.version_major == 2) {
+        if (header.flags & 0x3f) {
+            // We only support the 2 high bits, if any of the lower bits are
+            // set, we cannot guarantee to understand the tag format.
+            return false;
+        }
+
+        if (header.flags & 0x40) {
+            // No compression scheme has been decided yet, ignore the
+            // tag if compression is indicated.
+
+            return false;
+        }
+    } else if (header.version_major == 3) {
+        if (header.flags & 0x1f) {
+            // We only support the 3 high bits, if any of the lower bits are
+            // set, we cannot guarantee to understand the tag format.
+            return false;
+        }
+    } else {
+        return false;
+    }
+
+    size_t size = 0;
+    for (int32_t i = 0; i < 4; ++i) {
+        if (header.enc_size[i] & 0x80) {
+            return false;
+        }
+
+        size = (size << 7) | header.enc_size[i];
+    }
+
+    mData = (uint8_t *)malloc(size);
+
+    if (mData == NULL) {
+        return false;
+    }
+
+    mSize = size;
+
+    if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
+        return false;
+    }
+
+    if (header.flags & 0x80) {
+        LOGI("removing unsynchronization");
+        removeUnsynchronization();
+    }
+
+    mFirstFrameOffset = 0;
+    if (header.version_major == 3 && (header.flags & 0x40)) {
+        // Version 2.3 has an optional extended header.
+
+        if (mSize < 4) {
+            return false;
+        }
+
+        size_t extendedHeaderSize = U32_AT(&mData[0]) + 4;
+
+        if (extendedHeaderSize > mSize) {
+            return false;
+        }
+
+        mFirstFrameOffset = extendedHeaderSize;
+
+        uint16_t extendedFlags = 0;
+        if (extendedHeaderSize >= 6) {
+            extendedFlags = U16_AT(&mData[4]);
+
+            if (extendedHeaderSize >= 10) {
+                size_t paddingSize = U32_AT(&mData[6]);
+
+                if (mFirstFrameOffset + paddingSize > mSize) {
+                    return false;
+                }
+
+                mSize -= paddingSize;
+            }
+
+            if (extendedFlags & 0x8000) {
+                LOGI("have crc");
+            }
+        }
+    }
+
+    if (header.version_major == 2) {
+        mVersion = ID3_V2_2;
+    } else {
+        CHECK_EQ(header.version_major, 3);
+        mVersion = ID3_V2_3;
+    }
+
+    return true;
+}
+
+void ID3::removeUnsynchronization() {
+    for (size_t i = 0; i + 1 < mSize; ++i) {
+        if (mData[i] == 0xff && mData[i + 1] == 0x00) {
+            memmove(&mData[i + 1], &mData[i + 2], mSize - i - 2);
+            --mSize;
+        }
+    }
+}
+
+ID3::Iterator::Iterator(const ID3 &parent, const char *id)
+    : mParent(parent),
+      mID(NULL),
+      mOffset(mParent.mFirstFrameOffset),
+      mFrameData(NULL),
+      mFrameSize(0) {
+    if (id) {
+        mID = strdup(id);
+    }
+
+    findFrame();
+}
+
+ID3::Iterator::~Iterator() {
+    if (mID) {
+        free(mID);
+        mID = NULL;
+    }
+}
+
+bool ID3::Iterator::done() const {
+    return mFrameData == NULL;
+}
+
+void ID3::Iterator::next() {
+    if (mFrameData == NULL) {
+        return;
+    }
+
+    mOffset += mFrameSize;
+
+    findFrame();
+}
+
+void ID3::Iterator::getID(String8 *id) const {
+    id->setTo("");
+
+    if (mFrameData == NULL) {
+        return;
+    }
+
+    if (mParent.mVersion == ID3_V2_2) {
+        id->setTo((const char *)&mParent.mData[mOffset], 3);
+    } else {
+        CHECK_EQ(mParent.mVersion, ID3_V2_3);
+        id->setTo((const char *)&mParent.mData[mOffset], 4);
+    }
+}
+
+static void convertISO8859ToString8(
+        const uint8_t *data, size_t size,
+        String8 *s) {
+    size_t utf8len = 0;
+    for (size_t i = 0; i < size; ++i) {
+        if (data[i] < 0x80) {
+            ++utf8len;
+        } else {
+            utf8len += 2;
+        }
+    }
+
+    if (utf8len == size) {
+        // Only ASCII characters present.
+
+        s->setTo((const char *)data, size);
+        return;
+    }
+
+    char *tmp = new char[utf8len];
+    char *ptr = tmp;
+    for (size_t i = 0; i < size; ++i) {
+        if (data[i] < 0x80) {
+            *ptr++ = data[i];
+        } else if (data[i] < 0xc0) {
+            *ptr++ = 0xc2;
+            *ptr++ = data[i];
+        } else {
+            *ptr++ = 0xc3;
+            *ptr++ = data[i] - 64;
+        }
+    }
+
+    s->setTo(tmp, utf8len);
+
+    delete[] tmp;
+    tmp = NULL;
+}
+
+void ID3::Iterator::getString(String8 *id) const {
+    id->setTo("");
+
+    if (mFrameData == NULL) {
+        return;
+    }
+
+    size_t n = mFrameSize - getHeaderLength() - 1;
+
+    if (*mFrameData == 0x00) {
+        // ISO 8859-1
+        convertISO8859ToString8(mFrameData + 1, n, id);
+    } else {
+        // UCS-2
+        id->setTo((const char16_t *)(mFrameData + 1), n);
+    }
+}
+
+const uint8_t *ID3::Iterator::getData(size_t *length) const {
+    *length = 0;
+
+    if (mFrameData == NULL) {
+        return NULL;
+    }
+
+    *length = mFrameSize - getHeaderLength();
+
+    return mFrameData;
+}
+
+size_t ID3::Iterator::getHeaderLength() const {
+    if (mParent.mVersion == ID3_V2_2) {
+        return 6;
+    } else {
+        CHECK_EQ(mParent.mVersion, ID3_V2_3);
+        return 10;
+    }
+}
+
+void ID3::Iterator::findFrame() {
+    for (;;) {
+        mFrameData = NULL;
+        mFrameSize = 0;
+
+        if (mParent.mVersion == ID3_V2_2) {
+            if (mOffset + 6 > mParent.mSize) {
+                return;
+            }
+
+            if (!memcmp(&mParent.mData[mOffset], "\0\0\0", 3)) {
+                return;
+            }
+
+            mFrameSize =
+                (mParent.mData[mOffset + 3] << 16)
+                | (mParent.mData[mOffset + 4] << 8)
+                | mParent.mData[mOffset + 5];
+
+            mFrameSize += 6;
+
+            if (mOffset + mFrameSize > mParent.mSize) {
+                LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
+                     mOffset, mFrameSize, mParent.mSize - mOffset - 6);
+                return;
+            }
+
+            mFrameData = &mParent.mData[mOffset + 6];
+
+            if (!mID) {
+                break;
+            }
+
+            char id[4];
+            memcpy(id, &mParent.mData[mOffset], 3);
+            id[3] = '\0';
+
+            if (!strcmp(id, mID)) {
+                break;
+            }
+        } else {
+            CHECK_EQ(mParent.mVersion, ID3_V2_3);
+
+            if (mOffset + 10 > mParent.mSize) {
+                return;
+            }
+
+            if (!memcmp(&mParent.mData[mOffset], "\0\0\0\0", 4)) {
+                return;
+            }
+
+            mFrameSize = 10 + U32_AT(&mParent.mData[mOffset + 4]);
+
+            if (mOffset + mFrameSize > mParent.mSize) {
+                LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
+                     mOffset, mFrameSize, mParent.mSize - mOffset - 10);
+                return;
+            }
+
+            mFrameData = &mParent.mData[mOffset + 10];
+
+            if (!mID) {
+                break;
+            }
+
+            char id[5];
+            memcpy(id, &mParent.mData[mOffset], 4);
+            id[4] = '\0';
+
+            if (!strcmp(id, mID)) {
+                break;
+            }
+        }
+
+        mOffset += mFrameSize;
+    }
+}
+
+static size_t StringSize(const uint8_t *start, uint8_t encoding) {
+    if (encoding== 0x00) {
+        // ISO 8859-1
+        return strlen((const char *)start) + 1;
+    }
+
+    // UCS-2
+    size_t n = 0;
+    while (start[n] != '\0' || start[n + 1] != '\0') {
+        n += 2;
+    }
+
+    return n;
+}
+
+const void *
+ID3::getAlbumArt(size_t *length, String8 *mime) const {
+    *length = 0;
+    mime->setTo("");
+
+    Iterator it(*this, mVersion == ID3_V2_3 ? "APIC" : "PIC");
+
+    while (!it.done()) {
+        size_t size;
+        const uint8_t *data = it.getData(&size);
+
+        if (mVersion == ID3_V2_3) {
+            uint8_t encoding = data[0];
+            mime->setTo((const char *)&data[1]);
+            size_t mimeLen = strlen((const char *)&data[1]) + 1;
+
+            uint8_t picType = data[1 + mimeLen];
+#if 0
+            if (picType != 0x03) {
+                // Front Cover Art
+                it.next();
+                continue;
+            }
+#endif
+
+            size_t descLen = StringSize(&data[2 + mimeLen], encoding);
+
+            *length = size - 2 - mimeLen - descLen;
+
+            return &data[2 + mimeLen + descLen];
+        } else {
+            uint8_t encoding = data[0];
+
+            if (!memcmp(&data[1], "PNG", 3)) {
+                mime->setTo("image/png");
+            } else if (!memcmp(&data[1], "JPG", 3)) {
+                mime->setTo("image/jpeg");
+            } else if (!memcmp(&data[1], "-->", 3)) {
+                mime->setTo("text/plain");
+            } else {
+                return NULL;
+            }
+
+#if 0
+            uint8_t picType = data[4];
+            if (picType != 0x03) {
+                // Front Cover Art
+                it.next();
+                continue;
+            }
+#endif
+
+            size_t descLen = StringSize(&data[5], encoding);
+
+            *length = size - 5 - descLen;
+
+            return &data[5 + descLen];
+        }
+    }
+
+    return NULL;
+}
+
+
+}  // namespace android
diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp
new file mode 100644
index 0000000..305b065
--- /dev/null
+++ b/media/libstagefright/id3/testid3.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../include/ID3.h"
+
+#include <ctype.h>
+#include <dirent.h>
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaDebug.h>
+
+#define MAXPATHLEN 256
+
+using namespace android;
+
+static void hexdump(const void *_data, size_t size) {
+    const uint8_t *data = (const uint8_t *)_data;
+    size_t offset = 0;
+    while (offset < size) {
+        printf("0x%04x  ", offset);
+
+        size_t n = size - offset;
+        if (n > 16) {
+            n = 16;
+        }
+
+        for (size_t i = 0; i < 16; ++i) {
+            if (i == 8) {
+                printf(" ");
+            }
+
+            if (offset + i < size) {
+                printf("%02x ", data[offset + i]);
+            } else {
+                printf("   ");
+            }
+        }
+
+        printf(" ");
+
+        for (size_t i = 0; i < n; ++i) {
+            if (isprint(data[offset + i])) {
+                printf("%c", data[offset + i]);
+            } else {
+                printf(".");
+            }
+        }
+
+        printf("\n");
+
+        offset += 16;
+    }
+}
+
+void scanFile(const char *path) {
+    sp<FileSource> file = new FileSource(path);
+    CHECK_EQ(file->initCheck(), OK);
+
+    ID3 tag(file);
+    if (!tag.isValid()) {
+        printf("FAIL %s\n", path);
+    } else {
+        printf("SUCCESS %s\n", path);
+
+        ID3::Iterator it(tag, NULL);
+        while (!it.done()) {
+            String8 id;
+            it.getID(&id);
+
+            CHECK(id.length() > 0);
+            if (id[0] == 'T') {
+                String8 text;
+                it.getString(&text);
+
+                printf("  found text frame '%s': %s\n", id.string(), text.string());
+            } else {
+                printf("  found frame '%s'.\n", id.string());
+            }
+
+            it.next();
+        }
+
+        size_t dataSize;
+        String8 mime;
+        const void *data = tag.getAlbumArt(&dataSize, &mime);
+
+        if (data) {
+            printf("found album art: size=%d mime='%s'\n", dataSize,
+                   mime.string());
+
+            hexdump(data, dataSize > 128 ? 128 : dataSize);
+        }
+    }
+}
+
+void scan(const char *path) {
+    DIR *dir = opendir(path);
+
+    if (dir == NULL) {
+        return;
+    }
+
+    rewinddir(dir);
+
+    struct dirent *ent;
+    while ((ent = readdir(dir)) != NULL) {
+        if (!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name)) {
+            continue;
+        }
+
+        char newPath[MAXPATHLEN];
+        strcpy(newPath, path);
+        strcat(newPath, "/");
+        strcat(newPath, ent->d_name);
+
+        if (ent->d_type == DT_DIR) {
+            scan(newPath);
+        } else if (ent->d_type == DT_REG) {
+            size_t len = strlen(ent->d_name);
+
+            if (len >= 4
+                && !strcasecmp(ent->d_name + len - 4, ".mp3")) {
+                scanFile(newPath);
+            }
+        }
+    }
+
+    closedir(dir);
+    dir = NULL;
+}
+
+int main(int argc, char **argv) {
+    android::ProcessState::self()->startThreadPool();
+
+    DataSource::RegisterDefaultSniffers();
+
+    for (int i = 1; i < argc; ++i) {
+        scan(argv[i]);
+    }
+
+    return 0;
+}
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
new file mode 100644
index 0000000..79931ac
--- /dev/null
+++ b/media/libstagefright/include/ID3.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ID3_H_
+
+#define ID3_H_
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct DataSource;
+struct String8;
+
+struct ID3 {
+    enum Version {
+        ID3_UNKNOWN,
+        ID3_V2_2,
+        ID3_V2_3
+    };
+
+    ID3(const sp<DataSource> &source);
+    ~ID3();
+
+    bool isValid() const;
+
+    Version version() const;
+
+    const void *getAlbumArt(size_t *length, String8 *mime) const;
+
+    struct Iterator {
+        Iterator(const ID3 &parent, const char *id);
+        ~Iterator();
+
+        bool done() const;
+        void getID(String8 *id) const;
+        void getString(String8 *s) const;
+        const uint8_t *getData(size_t *length) const;
+        void next();
+
+    private:
+        const ID3 &mParent;
+        char *mID;
+        size_t mOffset;
+
+        const uint8_t *mFrameData;
+        size_t mFrameSize;
+
+        void findFrame();
+
+        size_t getHeaderLength() const;
+
+        Iterator(const Iterator &);
+        Iterator &operator=(const Iterator &);
+    };
+
+private:
+    bool mIsValid;
+    uint8_t *mData;
+    size_t mSize;
+    size_t mFirstFrameOffset;
+    Version mVersion;
+
+    bool parse(const sp<DataSource> &source);
+    void removeUnsynchronization();
+
+    ID3(const ID3 &);
+    ID3 &operator=(const ID3 &);
+};
+
+}  // namespace android
+
+#endif  // ID3_H_
+
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
index b5a6b3c..3ce6df3 100644
--- a/media/libstagefright/include/MP3Extractor.h
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -34,6 +34,8 @@
     virtual sp<MediaSource> getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
+    virtual sp<MetaData> getMetaData();
+
 protected:
     virtual ~MP3Extractor();
 
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index 16127d7..b80387f 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -1,19 +1,18 @@
 /*
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 #ifndef STAGEFRIGHT_METADATA_RETRIEVER_H_
 
@@ -22,9 +21,11 @@
 #include <media/MediaMetadataRetrieverInterface.h>
 
 #include <media/stagefright/OMXClient.h>
+#include <utils/KeyedVector.h>
 
 namespace android {
 
+struct DataSource;
 class MediaExtractor;
 
 struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface {
@@ -40,8 +41,15 @@
 
 private:
     OMXClient mClient;
+    sp<DataSource> mSource;
     sp<MediaExtractor> mExtractor;
 
+    bool mParsedMetaData;
+    KeyedVector<int, String8> mMetaData;
+    MediaAlbumArt *mAlbumArt;
+
+    void parseMetaData();
+
     StagefrightMetadataRetriever(const StagefrightMetadataRetriever &);
 
     StagefrightMetadataRetriever &operator=(