- Support comfort noise in AMRExtractor
- Support duration and seeking in AMRExtractor for different bit rates

Bug 2530101

Change-Id: I12beffea73cea0ec056f0e0cf51a4a8d46d897b8
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index ac87c29..7eca5e4 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -35,8 +35,9 @@
 public:
     AMRSource(const sp<DataSource> &source,
               const sp<MetaData> &meta,
-              size_t frameSize,
-              bool isWide);
+              bool isWide,
+              const off64_t *offset_table,
+              size_t offset_table_length);
 
     virtual status_t start(MetaData *params = NULL);
     virtual status_t stop();
@@ -52,7 +53,6 @@
 private:
     sp<DataSource> mDataSource;
     sp<MetaData> mMeta;
-    size_t mFrameSize;
     bool mIsWide;
 
     off64_t mOffset;
@@ -60,6 +60,9 @@
     bool mStarted;
     MediaBufferGroup *mGroup;
 
+    off64_t mOffsetTable[OFFSET_TABLE_LEN];
+    size_t mOffsetTableLength;
+
     AMRSource(const AMRSource &);
     AMRSource &operator=(const AMRSource &);
 };
@@ -67,13 +70,25 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 static size_t getFrameSize(bool isWide, unsigned FT) {
-    static const size_t kFrameSizeNB[8] = {
-        95, 103, 118, 134, 148, 159, 204, 244
+    static const size_t kFrameSizeNB[16] = {
+        95, 103, 118, 134, 148, 159, 204, 244,
+        39, 43, 38, 37, // SID
+        0, 0, 0, // future use
+        0 // no data
     };
-    static const size_t kFrameSizeWB[9] = {
-        132, 177, 253, 285, 317, 365, 397, 461, 477
+    static const size_t kFrameSizeWB[16] = {
+        132, 177, 253, 285, 317, 365, 397, 461, 477,
+        40, // SID
+        0, 0, 0, 0, // future use
+        0, // speech lost
+        0 // no data
     };
 
+    if (FT > 15 || (isWide && FT > 9 && FT < 14) || (!isWide && FT > 11 && FT < 15)) {
+        LOGE("illegal AMR frame type %d", FT);
+        return 0;
+    }
+
     size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
 
     // Round up bits to bytes and add 1 for the header byte.
@@ -82,9 +97,26 @@
     return frameSize;
 }
 
+static status_t getFrameSizeByOffset(const sp<DataSource> &source,
+        off64_t offset, bool isWide, size_t *frameSize) {
+    uint8_t header;
+    if (source->readAt(offset, &header, 1) < 1) {
+        return ERROR_IO;
+    }
+
+    unsigned FT = (header >> 3) & 0x0f;
+
+    *frameSize = getFrameSize(isWide, FT);
+    if (*frameSize == 0) {
+        return ERROR_MALFORMED;
+    }
+    return OK;
+}
+
 AMRExtractor::AMRExtractor(const sp<DataSource> &source)
     : mDataSource(source),
-      mInitCheck(NO_INIT) {
+      mInitCheck(NO_INIT),
+      mOffsetTableLength(0) {
     String8 mimeType;
     float confidence;
     if (!SniffAMR(mDataSource, &mimeType, &confidence, NULL)) {
@@ -101,25 +133,29 @@
     mMeta->setInt32(kKeyChannelCount, 1);
     mMeta->setInt32(kKeySampleRate, mIsWide ? 16000 : 8000);
 
-    size_t offset = mIsWide ? 9 : 6;
-    uint8_t header;
-    if (mDataSource->readAt(offset, &header, 1) != 1) {
-        return;
-    }
-
-    unsigned FT = (header >> 3) & 0x0f;
-
-    if (FT > 8 || (!mIsWide && FT > 7)) {
-        return;
-    }
-
-    mFrameSize = getFrameSize(mIsWide, FT);
-
+    off64_t offset = mIsWide ? 9 : 6;
     off64_t streamSize;
-    if (mDataSource->getSize(&streamSize) == OK) {
-        off64_t numFrames = streamSize / mFrameSize;
+    size_t frameSize, numFrames = 0;
+    int64_t duration = 0;
 
-        mMeta->setInt64(kKeyDuration, 20000ll * numFrames);
+    if (mDataSource->getSize(&streamSize) == OK) {
+         while (offset < streamSize) {
+            if (getFrameSizeByOffset(source, offset, mIsWide, &frameSize) != OK) {
+                return;
+            }
+
+            if ((numFrames % 50 == 0) && (numFrames / 50 < OFFSET_TABLE_LEN)) {
+                CHECK_EQ(mOffsetTableLength, numFrames / 50);
+                mOffsetTable[mOffsetTableLength] = offset - (mIsWide ? 9: 6);
+                mOffsetTableLength ++;
+            }
+
+            offset += frameSize;
+            duration += 20000;  // Each frame is 20ms
+            numFrames ++;
+        }
+
+        mMeta->setInt64(kKeyDuration, duration);
     }
 
     mInitCheck = OK;
@@ -149,7 +185,8 @@
         return NULL;
     }
 
-    return new AMRSource(mDataSource, mMeta, mFrameSize, mIsWide);
+    return new AMRSource(mDataSource, mMeta, mIsWide,
+            mOffsetTable, mOffsetTableLength);
 }
 
 sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) {
@@ -164,15 +201,18 @@
 
 AMRSource::AMRSource(
         const sp<DataSource> &source, const sp<MetaData> &meta,
-        size_t frameSize, bool isWide)
+        bool isWide, const off64_t *offset_table, size_t offset_table_length)
     : mDataSource(source),
       mMeta(meta),
-      mFrameSize(frameSize),
       mIsWide(isWide),
       mOffset(mIsWide ? 9 : 6),
       mCurrentTimeUs(0),
       mStarted(false),
-      mGroup(NULL) {
+      mGroup(NULL),
+      mOffsetTableLength(offset_table_length) {
+    if (mOffsetTableLength > 0 && mOffsetTableLength <= OFFSET_TABLE_LEN) {
+        memcpy ((char*)mOffsetTable, (char*)offset_table, sizeof(off64_t) * mOffsetTableLength);
+    }
 }
 
 AMRSource::~AMRSource() {
@@ -214,9 +254,25 @@
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
     if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+        size_t size;
         int64_t seekFrame = seekTimeUs / 20000ll;  // 20ms per frame.
         mCurrentTimeUs = seekFrame * 20000ll;
-        mOffset = seekFrame * mFrameSize + (mIsWide ? 9 : 6);
+
+        int index = seekFrame / 50;
+        if (index >= mOffsetTableLength) {
+            index = mOffsetTableLength - 1;
+        }
+
+        mOffset = mOffsetTable[index] + (mIsWide ? 9 : 6);
+
+        for (int i = 0; i< seekFrame - index * 50; i++) {
+            status_t err;
+            if ((err = getFrameSizeByOffset(mDataSource, mOffset,
+                            mIsWide, &size)) != OK) {
+                return err;
+            }
+            mOffset += size;
+        }
     }
 
     uint8_t header;
@@ -236,16 +292,11 @@
 
     unsigned FT = (header >> 3) & 0x0f;
 
-    if (FT > 8 || (!mIsWide && FT > 7)) {
-
-        LOGE("illegal AMR frame type %d", FT);
-
+    size_t frameSize = getFrameSize(mIsWide, FT);
+    if (frameSize == 0) {
         return ERROR_MALFORMED;
     }
 
-    size_t frameSize = getFrameSize(mIsWide, FT);
-    CHECK_EQ(frameSize, mFrameSize);
-
     MediaBuffer *buffer;
     status_t err = mGroup->acquire_buffer(&buffer);
     if (err != OK) {
diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h
index 1cdf36d..589d837 100644
--- a/media/libstagefright/include/AMRExtractor.h
+++ b/media/libstagefright/include/AMRExtractor.h
@@ -24,6 +24,7 @@
 
 struct AMessage;
 class String8;
+#define OFFSET_TABLE_LEN    300
 
 class AMRExtractor : public MediaExtractor {
 public:
@@ -42,9 +43,11 @@
     sp<DataSource> mDataSource;
     sp<MetaData> mMeta;
     status_t mInitCheck;
-    size_t mFrameSize;
     bool mIsWide;
 
+    off64_t mOffsetTable[OFFSET_TABLE_LEN]; //5 min
+    size_t mOffsetTableLength;
+
     AMRExtractor(const AMRExtractor &);
     AMRExtractor &operator=(const AMRExtractor &);
 };