Added a .amr file extractor (for AMR-NB and AMR-WB content).
diff --git a/include/media/stagefright/AMRExtractor.h b/include/media/stagefright/AMRExtractor.h
new file mode 100644
index 0000000..c8710d3
--- /dev/null
+++ b/include/media/stagefright/AMRExtractor.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef AMR_EXTRACTOR_H_
+
+#define AMR_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class String8;
+
+class AMRExtractor : public MediaExtractor {
+public:
+    AMRExtractor(const sp<DataSource> &source);
+
+    virtual size_t countTracks();
+    virtual sp<MediaSource> getTrack(size_t index);
+    virtual sp<MetaData> getTrackMetaData(size_t index);
+
+    static sp<MetaData> makeAMRFormat(bool isWide);
+
+protected:
+    virtual ~AMRExtractor();
+
+private:
+    sp<DataSource> mDataSource;
+    status_t mInitCheck;
+    bool mIsWide;
+
+    AMRExtractor(const AMRExtractor &);
+    AMRExtractor &operator=(const AMRExtractor &);
+};
+
+bool SniffAMR(
+        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+
+}  // namespace android
+
+#endif  // AMR_EXTRACTOR_H_
diff --git a/include/media/stagefright/MP3Extractor.h b/include/media/stagefright/MP3Extractor.h
index 4e1f3c3..11ba01d 100644
--- a/include/media/stagefright/MP3Extractor.h
+++ b/include/media/stagefright/MP3Extractor.h
@@ -30,9 +30,9 @@
     // Extractor assumes ownership of "source".
     MP3Extractor(const sp<DataSource> &source);
 
-    size_t countTracks();
-    sp<MediaSource> getTrack(size_t index);
-    sp<MetaData> getTrackMetaData(size_t index);
+    virtual size_t countTracks();
+    virtual sp<MediaSource> getTrack(size_t index);
+    virtual sp<MetaData> getTrackMetaData(size_t index);
 
 protected:
     virtual ~MP3Extractor();
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
new file mode 100644
index 0000000..4772aca
--- /dev/null
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -0,0 +1,238 @@
+/*
+ * 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 "AMRExtractor"
+#include <utils/Log.h>
+
+#include <media/stagefright/AMRExtractor.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class AMRSource : public MediaSource {
+public:
+    AMRSource(const sp<DataSource> &source, bool isWide);
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+    virtual ~AMRSource();
+
+private:
+    sp<DataSource> mDataSource;
+    bool mIsWide;
+
+    off_t mOffset;
+    int64_t mCurrentTimeUs;
+    bool mStarted;
+    MediaBufferGroup *mGroup;
+
+    AMRSource(const AMRSource &);
+    AMRSource &operator=(const AMRSource &);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+AMRExtractor::AMRExtractor(const sp<DataSource> &source)
+    : mDataSource(source),
+      mInitCheck(NO_INIT) {
+    String8 mimeType;
+    float confidence;
+    if (SniffAMR(mDataSource, &mimeType, &confidence)) {
+        mInitCheck = OK;
+        mIsWide = (mimeType == "audio/amr-wb");
+    }
+}
+
+AMRExtractor::~AMRExtractor() {
+}
+
+size_t AMRExtractor::countTracks() {
+    return mInitCheck == OK ? 1 : 0;
+}
+
+sp<MediaSource> AMRExtractor::getTrack(size_t index) {
+    if (mInitCheck != OK || index != 0) {
+        return NULL;
+    }
+
+    return new AMRSource(mDataSource, mIsWide);
+}
+
+sp<MetaData> AMRExtractor::getTrackMetaData(size_t index) {
+    if (mInitCheck != OK || index != 0) {
+        return NULL;
+    }
+
+    return makeAMRFormat(mIsWide);
+}
+
+// static
+sp<MetaData> AMRExtractor::makeAMRFormat(bool isWide) {
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, isWide ? "audio/amr-wb" : "audio/3gpp");
+    meta->setInt32(kKeyChannelCount, 1);
+    meta->setInt32(kKeySampleRate, isWide ? 16000 : 8000);
+
+    return meta;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+AMRSource::AMRSource(const sp<DataSource> &source, bool isWide)
+    : mDataSource(source),
+      mIsWide(isWide),
+      mOffset(mIsWide ? 9 : 6),
+      mCurrentTimeUs(0),
+      mStarted(false),
+      mGroup(NULL) {
+}
+
+AMRSource::~AMRSource() {
+    if (mStarted) {
+        stop();
+    }
+}
+
+status_t AMRSource::start(MetaData *params) {
+    CHECK(!mStarted);
+
+    mOffset = mIsWide ? 9 : 6;
+    mCurrentTimeUs = 0;
+    mGroup = new MediaBufferGroup;
+    mGroup->add_buffer(new MediaBuffer(128));
+    mStarted = true;
+
+    return OK;
+}
+
+status_t AMRSource::stop() {
+    CHECK(mStarted);
+
+    delete mGroup;
+    mGroup = NULL;
+
+    mStarted = false;
+    return OK;
+}
+
+sp<MetaData> AMRSource::getFormat() {
+    return AMRExtractor::makeAMRFormat(mIsWide);
+}
+
+status_t AMRSource::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    *out = NULL;
+
+    uint8_t header;
+    ssize_t n = mDataSource->read_at(mOffset, &header, 1);
+
+    if (n < 1) {
+        return ERROR_IO;
+    }
+
+    MediaBuffer *buffer;
+    status_t err = mGroup->acquire_buffer(&buffer);
+    if (err != OK) {
+        return err;
+    }
+
+    if (header & 0x83) {
+        // Padding bits must be 0.
+
+        return ERROR_MALFORMED;
+    }
+
+    unsigned FT = (header >> 3) & 0x0f;
+
+    if (FT > 8 || (!mIsWide && FT > 7)) {
+        return ERROR_MALFORMED;
+    }
+
+    static const size_t kFrameSizeNB[8] = {
+        95, 103, 118, 134, 148, 159, 204, 244
+    };
+    static const size_t kFrameSizeWB[9] = {
+        132, 177, 253, 285, 317, 365, 397, 461, 477
+    };
+
+    size_t frameSize = mIsWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
+
+    // Round up bits to bytes and add 1 for the header byte.
+    frameSize = (frameSize + 7) / 8 + 1;
+
+    n = mDataSource->read_at(mOffset, buffer->data(), frameSize);
+
+    if (n != (ssize_t)frameSize) {
+        buffer->release();
+        buffer = NULL;
+
+        return ERROR_IO;
+    }
+
+    buffer->set_range(0, frameSize);
+    buffer->meta_data()->setInt32(
+            kKeyTimeUnits, (mCurrentTimeUs + 500) / 1000);
+    buffer->meta_data()->setInt32(
+            kKeyTimeScale, 1000);
+
+    mOffset += frameSize;
+    mCurrentTimeUs += 20000;  // Each frame is 20ms
+
+    *out = buffer;
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool SniffAMR(
+        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+    char header[9];
+
+    if (source->read_at(0, header, sizeof(header)) != sizeof(header)) {
+        return false;
+    }
+
+    if (!memcmp(header, "#!AMR\n", 6)) {
+        *mimeType = "audio/3gpp";
+        *confidence = 0.5;
+
+        return true;
+    } else if (!memcmp(header, "#!AMR-WB\n", 9)) {
+        *mimeType = "audio/amr-wb";
+        *confidence = 0.5;
+
+        return true;
+    }
+
+    return false;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 20b0da2..c3a4722 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=                 \
+        AMRExtractor.cpp          \
         CachingDataSource.cpp     \
         DataSource.cpp            \
         FileSource.cpp            \
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 02a276b..daac539 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <media/stagefright/AMRExtractor.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MP3Extractor.h>
@@ -84,6 +85,7 @@
 void DataSource::RegisterDefaultSniffers() {
     RegisterSniffer(SniffMP3);
     RegisterSniffer(SniffMPEG4);
+    RegisterSniffer(SniffAMR);
 }
 
 }  // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 5f78e12..8afa8e1 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "MediaExtractor"
 #include <utils/Log.h>
 
+#include <media/stagefright/AMRExtractor.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MP3Extractor.h>
 #include <media/stagefright/MPEG4Extractor.h>
@@ -47,6 +48,9 @@
         return new MPEG4Extractor(source);
     } else if (!strcasecmp(mime, "audio/mpeg")) {
         return new MP3Extractor(source);
+    } else if (!strcasecmp(mime, "audio/3gpp")
+            || !strcasecmp(mime, "audio/amr-wb")) {
+        return new AMRExtractor(source);
     }
 
     return NULL;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 3cba142..b1e7069 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -50,6 +50,7 @@
     { "audio/3gpp", "OMX.TI.AMR.decode" },
     { "audio/3gpp", "OMX.PV.amrdec" },
     { "audio/amr-wb", "OMX.TI.WBAMR.decode" },
+    { "audio/amr-wb", "OMX.PV.amrdec" },
     { "audio/mp4a-latm", "OMX.TI.AAC.decode" },
     { "audio/mp4a-latm", "OMX.PV.aacdec" },
     { "video/mp4v-es", "OMX.qcom.video.decoder.mpeg4" },