Merge "AudioFlinger: fix offload volume on resume" into nyc-mr1-dev
diff --git a/include/media/IMediaSource.h b/include/media/IMediaSource.h
index 524e7aa..2ff42ec 100644
--- a/include/media/IMediaSource.h
+++ b/include/media/IMediaSource.h
@@ -18,14 +18,17 @@
 
 #define IMEDIA_SOURCE_BASE_H_
 
+#include <map>
+
 #include <binder/IInterface.h>
+#include <binder/IMemory.h>
+#include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaErrors.h>
 
 namespace android {
 
 struct MediaSource;
 class MetaData;
-class MediaBuffer;
 class MediaBufferGroup;
 
 class IMediaSource : public IInterface {
@@ -56,7 +59,7 @@
     // a) not request a seek
     // b) not be late, i.e. lateness_us = 0
     struct ReadOptions {
-        enum SeekMode {
+        enum SeekMode : int32_t {
             SEEK_PREVIOUS_SYNC,
             SEEK_NEXT_SYNC,
             SEEK_CLOSEST_SYNC,
@@ -72,6 +75,7 @@
         void clearSeekTo();
         bool getSeekTo(int64_t *time_us, SeekMode *mode) const;
 
+        // TODO: remove this if unused.
         void setLateBy(int64_t lateness_us);
         int64_t getLateBy() const;
 
@@ -79,6 +83,11 @@
         void clearNonBlocking();
         bool getNonBlocking() const;
 
+        // Used to clear all non-persistent options for multiple buffer reads.
+        void clearNonPersistent() {
+            clearSeekTo();
+        }
+
     private:
         enum Options {
             kSeekTo_Option      = 1,
@@ -98,25 +107,34 @@
     // A result of INFO_FORMAT_CHANGED indicates that the format of this
     // MediaSource has changed mid-stream, the client can continue reading
     // but should be prepared for buffers of the new configuration.
+    //
+    // TODO: consider removing read() in favor of readMultiple().
     virtual status_t read(
             MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
 
-    // Returns a vector of new buffers of data. The vector size could be
-    // <= |maxNumBuffers|. Used for buffers with small size
-    // since all buffer data are passed back by binder, not shared memory.
+    // Returns a vector of new buffers of data, where the new buffers are added
+    // to the end of the vector.
     // Call blocks until an error is encountered, or the end of the stream is
     // reached, or format change is hit, or |kMaxNumReadMultiple| buffers have
     // been read.
-    // End of stream is signalled by a result of ERROR_END_OF_STREAM.
+    // End of stream is signaled by a result of ERROR_END_OF_STREAM.
     // A result of INFO_FORMAT_CHANGED indicates that the format of this
     // MediaSource has changed mid-stream, the client can continue reading
     // but should be prepared for buffers of the new configuration.
+    //
+    // ReadOptions may be specified. Persistent options apply to all reads;
+    // non-persistent options (e.g. seek) apply only to the first read.
     virtual status_t readMultiple(
-            Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers = 1) = 0;
+            Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers = 1,
+            const ReadOptions *options = nullptr) = 0;
 
     // Returns true if |readMultiple| is supported, otherwise false.
     virtual bool supportReadMultiple() = 0;
 
+    // Returns true if |read| supports nonblocking option, otherwise false.
+    // |readMultiple| if supported, always allows the nonblocking option.
+    virtual bool supportNonblockingRead() = 0;
+
     // Causes this source to suspend pulling data from its upstream source
     // until a subsequent read-with-seek. Currently only supported by
     // OMXCodec.
@@ -147,21 +165,102 @@
         return ERROR_UNSUPPORTED;
     }
 
+    // TODO: Implement this for local media sources.
     virtual status_t readMultiple(
-            Vector<MediaBuffer *> * /* buffers */, uint32_t /* maxNumBuffers = 1 */) {
+            Vector<MediaBuffer *> * /* buffers */, uint32_t /* maxNumBuffers = 1 */,
+            const ReadOptions * /* options = nullptr */) {
         return ERROR_UNSUPPORTED;
     }
 
     virtual bool supportReadMultiple() {
         return false;
     }
+
+    // Override in source if nonblocking reads are supported.
+    virtual bool supportNonblockingRead() {
+        return false;
+    }
+
+    static const size_t kBinderMediaBuffers = 4; // buffers managed by BnMediaSource
+    static const size_t kTransferSharedAsSharedThreshold = 4 * 1024;  // if >= shared, else inline
+    static const size_t kTransferInlineAsSharedThreshold = 64 * 1024; // if >= shared, else inline
+    static const size_t kInlineMaxTransfer = 256 * 1024; // Binder size limited to BINDER_VM_SIZE.
+
 protected:
     virtual ~BnMediaSource();
 
 private:
-    MediaBufferGroup *mGroup;
-};
+    uint32_t mBuffersSinceStop; // Buffer tracking variable
 
+    std::unique_ptr<MediaBufferGroup> mGroup;
+
+    // To prevent marshalling IMemory with each read transaction, we cache the IMemory pointer
+    // into a map.
+    //
+    // This is converted into an index, which is used to identify the associated memory
+    // on the receiving side.  We hold a reference to the IMemory here to ensure it doesn't
+    // change underneath us.
+
+    struct IndexCache {
+        IndexCache() : mIndex(0) { }
+
+        // Returns the index of the IMemory stored in cache or 0 if not found.
+        uint64_t lookup(const sp<IMemory> &mem) {
+            auto p = mMemoryToIndex.find(mem.get());
+            if (p == mMemoryToIndex.end()) {
+                return 0;
+            }
+            if (MediaBuffer::isDeadObject(p->second.first)) {
+                // this object's dead
+                ALOGW("Attempting to lookup a dead IMemory");
+                (void)mMemoryToIndex.erase(p);
+                return 0;
+            }
+            ALOGW_IF(p->second.first.get() != mem.get(), "Mismatched buffers without reset");
+            return p->second.second;
+        }
+
+        // Returns the index of the IMemory stored in the index cache.
+        uint64_t insert(const sp<IMemory> &mem) {
+            auto p = mMemoryToIndex.find(mem.get());
+            if (p == mMemoryToIndex.end()) {
+                if (mIndex == UINT64_MAX) {
+                    ALOGE("Index overflow");
+                    mIndex = 1; // skip overflow condition and hope for the best
+                } else {
+                    ++mIndex;
+                }
+                (void)mMemoryToIndex.emplace(// C++11 mem.get(), std::make_pair(mem, mIndex))
+                        std::piecewise_construct,
+                        std::forward_as_tuple(mem.get()), std::forward_as_tuple(mem, mIndex));
+                return mIndex;
+            }
+            ALOGW("IMemory already inserted into cache");
+            return p->second.second;
+        }
+
+        void reset() {
+            mMemoryToIndex.clear();
+            mIndex = 0;
+        }
+
+        void gc() {
+            for (auto it = mMemoryToIndex.begin(); it != mMemoryToIndex.end(); ) {
+                if (MediaBuffer::isDeadObject(it->second.first)) {
+                    it = mMemoryToIndex.erase(it);
+                } else {
+                    ++it;
+                }
+            }
+        }
+
+    private:
+        uint64_t mIndex;
+        // C++14 unordered_map erase on iterator is stable; C++11 has no guarantee.
+        // Could key on uintptr_t instead of IMemory *
+        std::map<IMemory *, std::pair<sp<IMemory>, uint64_t>> mMemoryToIndex;
+    } mIndexCache;
+};
 
 }  // namespace android
 
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 94d2896..25f7173 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -496,7 +496,7 @@
     status_t setupH263EncoderParameters(const sp<AMessage> &msg);
     status_t setupAVCEncoderParameters(const sp<AMessage> &msg);
     status_t setupHEVCEncoderParameters(const sp<AMessage> &msg);
-    status_t setupVPXEncoderParameters(const sp<AMessage> &msg);
+    status_t setupVPXEncoderParameters(const sp<AMessage> &msg, sp<AMessage> &outputFormat);
 
     status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level);
 
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
index 18b80e3..55b1f58 100644
--- a/include/media/stagefright/MediaBuffer.h
+++ b/include/media/stagefright/MediaBuffer.h
@@ -18,6 +18,8 @@
 
 #define MEDIA_BUFFER_H_
 
+#include <atomic>
+#include <list>
 #include <media/stagefright/foundation/MediaBufferBase.h>
 
 #include <pthread.h>
@@ -60,6 +62,12 @@
 
     MediaBuffer(const sp<ABuffer> &buffer);
 
+    MediaBuffer(const sp<IMemory> &mem) :
+        MediaBuffer((uint8_t *)mem->pointer() + sizeof(SharedControl), mem->size()) {
+        // delegate and override mMemory
+        mMemory = mem;
+    }
+
     // Decrements the reference count and returns the buffer to its
     // associated MediaBufferGroup if the reference count drops to 0.
     virtual void release();
@@ -91,9 +99,44 @@
 
     int refcount() const;
 
+    bool isDeadObject() const {
+        return isDeadObject(mMemory);
+    }
+
+    static bool isDeadObject(const sp<IMemory> &memory) {
+        if (memory.get() == nullptr || memory->pointer() == nullptr) return false;
+        return reinterpret_cast<SharedControl *>(memory->pointer())->isDeadObject();
+    }
+
 protected:
+    // MediaBuffer remote releases are handled through a
+    // pending release count variable stored in a SharedControl block
+    // at the start of the IMemory.
+
+    // Returns old value of pending release count.
+    inline int32_t addPendingRelease(int32_t value) {
+        return getSharedControl()->addPendingRelease(value);
+    }
+
+    // Issues all pending releases (works in parallel).
+    // Assumes there is a MediaBufferObserver.
+    inline void resolvePendingRelease() {
+        if (mMemory.get() == nullptr) return;
+        while (addPendingRelease(-1) > 0) {
+            release();
+        }
+        addPendingRelease(1);
+    }
+
+    // true if MediaBuffer is observed (part of a MediaBufferGroup).
+    inline bool isObserved() const {
+        return mObserver != nullptr;
+    }
+
     virtual ~MediaBuffer();
 
+    sp<IMemory> mMemory;
+
 private:
     friend class MediaBufferGroup;
     friend class OMXDecoder;
@@ -105,7 +148,6 @@
     void claim();
 
     MediaBufferObserver *mObserver;
-    MediaBuffer *mNextBuffer;
     int mRefCount;
 
     void *mData;
@@ -119,12 +161,57 @@
 
     MediaBuffer *mOriginal;
 
-    void setNextBuffer(MediaBuffer *buffer);
-    MediaBuffer *nextBuffer();
-
     MediaBuffer(const MediaBuffer &);
     MediaBuffer &operator=(const MediaBuffer &);
-    sp<IMemory> mMemory;
+
+    // SharedControl block at the start of IMemory.
+    struct SharedControl {
+        enum {
+            FLAG_DEAD_OBJECT = (1 << 0),
+        };
+
+        // returns old value
+        inline int32_t addPendingRelease(int32_t value) {
+            return std::atomic_fetch_add_explicit(
+                    &mPendingRelease, (int_least32_t)value, std::memory_order_seq_cst);
+        }
+
+        inline int32_t getPendingRelease() const {
+            return std::atomic_load_explicit(&mPendingRelease, std::memory_order_seq_cst);
+        }
+
+        inline void setPendingRelease(int32_t value) {
+            std::atomic_store_explicit(
+                    &mPendingRelease, (int_least32_t)value, std::memory_order_seq_cst);
+        }
+
+        inline bool isDeadObject() const {
+            return (std::atomic_load_explicit(
+                    &mFlags, std::memory_order_seq_cst) & FLAG_DEAD_OBJECT) != 0;
+        }
+
+        inline void setDeadObject() {
+            (void)std::atomic_fetch_or_explicit(
+                    &mFlags, (int_least32_t)FLAG_DEAD_OBJECT, std::memory_order_seq_cst);
+        }
+
+        inline void clear() {
+            std::atomic_store_explicit(
+                    &mFlags, (int_least32_t)0, std::memory_order_seq_cst);
+            std::atomic_store_explicit(
+                    &mPendingRelease, (int_least32_t)0, std::memory_order_seq_cst);
+        }
+
+    private:
+        // Caution: atomic_int_fast32_t is 64 bits on LP64.
+        std::atomic_int_least32_t mFlags;
+        std::atomic_int_least32_t mPendingRelease;
+        int32_t unused[6]; // additional buffer space
+    };
+
+    inline SharedControl *getSharedControl() const {
+         return reinterpret_cast<SharedControl *>(mMemory->pointer());
+     }
 };
 
 }  // namespace android
diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h
index 7ca3fa1..dfa31b2 100644
--- a/include/media/stagefright/MediaBufferGroup.h
+++ b/include/media/stagefright/MediaBufferGroup.h
@@ -29,11 +29,17 @@
 
 class MediaBufferGroup : public MediaBufferObserver {
 public:
-    MediaBufferGroup();
+    MediaBufferGroup(size_t growthLimit = 0);
+
+    // create a media buffer group with preallocated buffers
+    MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit = 0);
+
     ~MediaBufferGroup();
 
     void add_buffer(MediaBuffer *buffer);
 
+    bool has_buffers();
+
     // If nonBlocking is false, it blocks until a buffer is available and
     // passes it to the caller in *buffer, while returning OK.
     // The returned buffer will have a reference count of 1.
@@ -45,6 +51,11 @@
     status_t acquire_buffer(
             MediaBuffer **buffer, bool nonBlocking = false, size_t requestedSize = 0);
 
+    size_t buffers() const { return mBuffers.size(); }
+
+    // freeBuffers is the number of free buffers allowed to remain.
+    void gc(size_t freeBuffers = 0);
+
 protected:
     virtual void signalBufferReturned(MediaBuffer *buffer);
 
@@ -53,8 +64,8 @@
 
     Mutex mLock;
     Condition mCondition;
-
-    MediaBuffer *mFirstBuffer, *mLastBuffer;
+    size_t mGrowthLimit;  // Do not automatically grow group larger than this.
+    std::list<MediaBuffer *> mBuffers;
 
     MediaBufferGroup(const MediaBufferGroup &);
     MediaBufferGroup &operator=(const MediaBufferGroup &);
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 87c32a6..4b2b868 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -123,6 +123,9 @@
     bool findBuffer(const char *name, sp<ABuffer> *buffer) const;
     bool findMessage(const char *name, sp<AMessage> *obj) const;
 
+    // finds any numeric type cast to a float
+    bool findAsFloat(const char *name, float *value) const;
+
     bool findRect(
             const char *name,
             int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const;
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 2409157..fef430b 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -5,3 +5,4 @@
     group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct
     ioprio rt 4
     writepid /dev/cpuset/foreground/tasks
+    writepid /dev/stune/foreground/tasks
\ No newline at end of file
diff --git a/media/libmedia/IMediaSource.cpp b/media/libmedia/IMediaSource.cpp
index d2b4291..dd94ccf 100644
--- a/media/libmedia/IMediaSource.cpp
+++ b/media/libmedia/IMediaSource.cpp
@@ -36,79 +36,39 @@
     STOP,
     PAUSE,
     GETFORMAT,
-    READ,
+    // READ, deprecated
     READMULTIPLE,
-    RELEASE_BUFFER
+    RELEASE_BUFFER,
+    SUPPORT_NONBLOCKING_READ,
 };
 
 enum {
     NULL_BUFFER,
     SHARED_BUFFER,
-    INLINE_BUFFER
+    INLINE_BUFFER,
+    SHARED_BUFFER_INDEX,
 };
 
-class RemoteMediaBufferReleaser : public BBinder {
-public:
-    RemoteMediaBufferReleaser(MediaBuffer *buf, sp<BnMediaSource> owner) {
-        mBuf = buf;
-        mOwner = owner;
-    }
-    ~RemoteMediaBufferReleaser() {
-        if (mBuf) {
-            ALOGW("RemoteMediaBufferReleaser dtor called while still holding buffer");
-            mBuf->release();
-        }
-    }
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0) {
-        if (code == RELEASE_BUFFER) {
-            mBuf->release();
-            mBuf = NULL;
-            return OK;
-        } else {
-            return BBinder::onTransact(code, data, reply, flags);
-        }
-    }
-private:
-    MediaBuffer *mBuf;
-    // Keep a ref to ensure MediaBuffer is released before the owner, i.e., BnMediaSource,
-    // because BnMediaSource needs to delete MediaBufferGroup in its dtor and
-    // MediaBufferGroup dtor requires all MediaBuffer's have 0 ref count.
-    sp<BnMediaSource> mOwner;
-};
-
-
 class RemoteMediaBufferWrapper : public MediaBuffer {
 public:
-    RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source);
+    RemoteMediaBufferWrapper(const sp<IMemory> &mem)
+        : MediaBuffer(mem) {
+        ALOGV("RemoteMediaBufferWrapper: creating %p", this);
+    }
+
 protected:
-    virtual ~RemoteMediaBufferWrapper();
-private:
-    sp<IMemory> mMemory;
-    sp<IBinder> mRemoteSource;
+    virtual ~RemoteMediaBufferWrapper() {
+        // Indicate to MediaBufferGroup to release.
+        int32_t old = addPendingRelease(1);
+        ALOGV("RemoteMediaBufferWrapper: releasing %p, old %d", this, old);
+        mMemory.clear(); // don't set the dead object flag.
+    }
 };
 
-RemoteMediaBufferWrapper::RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source)
-: MediaBuffer(mem->pointer(), mem->size()) {
-    mMemory = mem;
-    mRemoteSource = source;
-}
-
-RemoteMediaBufferWrapper::~RemoteMediaBufferWrapper() {
-    mMemory.clear();
-    // Explicitly ask the remote side to release the buffer. We could also just clear
-    // mRemoteSource, but that doesn't immediately release the reference on the remote side.
-    Parcel data, reply;
-    mRemoteSource->transact(RELEASE_BUFFER, data, &reply);
-    mRemoteSource.clear();
-}
-
 class BpMediaSource : public BpInterface<IMediaSource> {
 public:
     BpMediaSource(const sp<IBinder>& impl)
-        : BpInterface<IMediaSource>(impl)
+        : BpInterface<IMediaSource>(impl), mBuffersSinceStop(0)
     {
     }
 
@@ -135,7 +95,10 @@
         ALOGV("stop");
         Parcel data, reply;
         data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
-        return remote()->transact(STOP, data, &reply);
+        status_t status = remote()->transact(STOP, data, &reply);
+        mMemoryCache.reset();
+        mBuffersSinceStop = 0;
+        return status;
     }
 
     virtual sp<MetaData> getFormat() {
@@ -151,46 +114,16 @@
     }
 
     virtual status_t read(MediaBuffer **buffer, const ReadOptions *options) {
-        ALOGV("read");
-        Parcel data, reply;
-        data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
-        if (options) {
-            data.writeByteArray(sizeof(*options), (uint8_t*) options);
-        }
-        status_t ret = remote()->transact(READ, data, &reply);
-        if (ret != NO_ERROR) {
-            return ret;
-        }
-        // wrap the returned data in a MediaBuffer
-        ret = reply.readInt32();
-        int32_t buftype = reply.readInt32();
-        if (buftype == SHARED_BUFFER) {
-            sp<IBinder> remote = reply.readStrongBinder();
-            sp<IBinder> binder = reply.readStrongBinder();
-            sp<IMemory> mem = interface_cast<IMemory>(binder);
-            if (mem == NULL) {
-                ALOGE("received NULL IMemory for shared buffer");
-            }
-            size_t offset = reply.readInt32();
-            size_t length = reply.readInt32();
-            MediaBuffer *buf = new RemoteMediaBufferWrapper(mem, remote);
-            buf->set_range(offset, length);
-            buf->meta_data()->updateFromParcel(reply);
-            *buffer = buf;
-        } else if (buftype == NULL_BUFFER) {
-            ALOGV("got status %d and NULL buffer", ret);
-            *buffer = NULL;
-        } else {
-            int32_t len = reply.readInt32();
-            ALOGV("got status %d and len %d", ret, len);
-            *buffer = new MediaBuffer(len);
-            reply.read((*buffer)->data(), len);
-            (*buffer)->meta_data()->updateFromParcel(reply);
-        }
+        Vector<MediaBuffer *> buffers;
+        status_t ret = readMultiple(&buffers, 1 /* maxNumBuffers */, options);
+        *buffer = buffers.size() == 0 ? nullptr : buffers[0];
+        ALOGV("read status %d, bufferCount %u, sinceStop %u",
+                ret, *buffer != nullptr, mBuffersSinceStop);
         return ret;
     }
 
-    virtual status_t readMultiple(Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers) {
+    virtual status_t readMultiple(
+            Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers, const ReadOptions *options) {
         ALOGV("readMultiple");
         if (buffers == NULL || !buffers->isEmpty()) {
             return BAD_VALUE;
@@ -198,33 +131,78 @@
         Parcel data, reply;
         data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
         data.writeUint32(maxNumBuffers);
+        if (options != nullptr) {
+            data.writeByteArray(sizeof(*options), (uint8_t*) options);
+        }
         status_t ret = remote()->transact(READMULTIPLE, data, &reply);
+        mMemoryCache.gc();
         if (ret != NO_ERROR) {
             return ret;
         }
         // wrap the returned data in a vector of MediaBuffers
-        int32_t bufCount = 0;
-        while (1) {
-            if (reply.readInt32() == 0) {
-                break;
+        int32_t buftype;
+        uint32_t bufferCount = 0;
+        while ((buftype = reply.readInt32()) != NULL_BUFFER) {
+            LOG_ALWAYS_FATAL_IF(bufferCount >= maxNumBuffers,
+                    "Received %u+ buffers and requested %u buffers",
+                    bufferCount + 1, maxNumBuffers);
+            MediaBuffer *buf;
+            if (buftype == SHARED_BUFFER || buftype == SHARED_BUFFER_INDEX) {
+                uint64_t index = reply.readUint64();
+                ALOGV("Received %s index %llu",
+                        buftype == SHARED_BUFFER ? "SHARED_BUFFER" : "SHARED_BUFFER_INDEX",
+                        (unsigned long long) index);
+                sp<IMemory> mem;
+                if (buftype == SHARED_BUFFER) {
+                    sp<IBinder> binder = reply.readStrongBinder();
+                    mem = interface_cast<IMemory>(binder);
+                    LOG_ALWAYS_FATAL_IF(mem.get() == nullptr,
+                            "Received NULL IMemory for shared buffer");
+                    mMemoryCache.insert(index, mem);
+                } else {
+                    mem = mMemoryCache.lookup(index);
+                    LOG_ALWAYS_FATAL_IF(mem.get() == nullptr,
+                            "Received invalid IMemory index for shared buffer: %llu",
+                            (unsigned long long)index);
+                }
+                size_t offset = reply.readInt32();
+                size_t length = reply.readInt32();
+                buf = new RemoteMediaBufferWrapper(mem);
+                buf->set_range(offset, length);
+                buf->meta_data()->updateFromParcel(reply);
+            } else { // INLINE_BUFFER
+                int32_t len = reply.readInt32();
+                ALOGV("INLINE_BUFFER status %d and len %d", ret, len);
+                buf = new MediaBuffer(len);
+                reply.read(buf->data(), len);
+                buf->meta_data()->updateFromParcel(reply);
             }
-            int32_t len = reply.readInt32();
-            ALOGV("got len %d", len);
-            MediaBuffer *buf = new MediaBuffer(len);
-            reply.read(buf->data(), len);
-            buf->meta_data()->updateFromParcel(reply);
             buffers->push_back(buf);
-            ++bufCount;
+            ++bufferCount;
+            ++mBuffersSinceStop;
         }
         ret = reply.readInt32();
-        ALOGV("got status %d, bufCount %d", ret, bufCount);
+        ALOGV("readMultiple status %d, bufferCount %u, sinceStop %u",
+                ret, bufferCount, mBuffersSinceStop);
         return ret;
     }
 
-    bool supportReadMultiple() {
+    // Binder proxy adds readMultiple support.
+    virtual bool supportReadMultiple() {
         return true;
     }
 
+    virtual bool supportNonblockingRead() {
+        ALOGV("supportNonblockingRead");
+        Parcel data, reply;
+        data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
+        status_t ret = remote()->transact(SUPPORT_NONBLOCKING_READ, data, &reply);
+        if (ret == NO_ERROR) {
+            return reply.readInt32() != 0;
+        }
+        return false;
+    }
+
     virtual status_t pause() {
         ALOGV("pause");
         Parcel data, reply;
@@ -238,10 +216,51 @@
     }
 
 private:
+
+    uint32_t mBuffersSinceStop; // Buffer tracking variable
+
     // NuPlayer passes pointers-to-metadata around, so we use this to keep the metadata alive
     // XXX: could we use this for caching, or does metadata change on the fly?
     sp<MetaData> mMetaData;
 
+    // Cache all IMemory objects received from MediaExtractor.
+    // We gc IMemory objects that are no longer active (referenced by a MediaBuffer).
+
+    struct MemoryCache {
+        sp<IMemory> lookup(uint64_t index) {
+            auto p = mIndexToMemory.find(index);
+            if (p == mIndexToMemory.end()) {
+                ALOGE("cannot find index!");
+                return nullptr;
+            }
+            return p->second;
+        }
+
+        void insert(uint64_t index, const sp<IMemory> &mem) {
+            if (mIndexToMemory.find(index) != mIndexToMemory.end()) {
+                ALOGE("index %llu already present", (unsigned long long)index);
+                return;
+            }
+            (void)mIndexToMemory.emplace(index, mem);
+        }
+
+        void reset() {
+            mIndexToMemory.clear();
+        }
+
+        void gc() {
+            for (auto it = mIndexToMemory.begin(); it != mIndexToMemory.end(); ) {
+                if (MediaBuffer::isDeadObject(it->second)) {
+                    it = mIndexToMemory.erase(it);
+                } else {
+                    ++it;
+                }
+            }
+        }
+    private:
+        // C++14 unordered_map erase on iterator is stable; C++11 has no guarantee.
+        std::map<uint64_t, sp<IMemory>> mIndexToMemory;
+    } mMemoryCache;
 };
 
 IMPLEMENT_META_INTERFACE(MediaSource, "android.media.IMediaSource");
@@ -250,12 +269,11 @@
 #define LOG_TAG "BnMediaSource"
 
 BnMediaSource::BnMediaSource()
-    : mGroup(NULL) {
+    : mBuffersSinceStop(0)
+    , mGroup(new MediaBufferGroup(kBinderMediaBuffers /* growthLimit */)) {
 }
 
 BnMediaSource::~BnMediaSource() {
-    delete mGroup;
-    mGroup = NULL;
 }
 
 status_t BnMediaSource::onTransact(
@@ -278,7 +296,11 @@
         case STOP: {
             ALOGV("stop");
             CHECK_INTERFACE(IMediaSource, data, reply);
-            return stop();
+            status_t status = stop();
+            mGroup->gc();
+            mIndexCache.reset();
+            mBuffersSinceStop = 0;
+            return status;
         }
         case PAUSE: {
             ALOGV("pause");
@@ -295,116 +317,129 @@
             }
             return UNKNOWN_ERROR;
         }
-        case READ: {
-            ALOGV("read");
-            CHECK_INTERFACE(IMediaSource, data, reply);
-            status_t ret;
-            MediaBuffer *buf = NULL;
-            ReadOptions opts;
-            uint32_t len;
-            if (data.readUint32(&len) == NO_ERROR &&
-                    len == sizeof(opts) && data.read((void*)&opts, len) == NO_ERROR) {
-                ret = read(&buf, &opts);
-            } else {
-                ret = read(&buf, NULL);
-            }
-
-            reply->writeInt32(ret);
-            if (buf != NULL) {
-                size_t usedSize = buf->range_length();
-                // even if we're using shared memory, we might not want to use it, since for small
-                // sizes it's faster to copy data through the Binder transaction
-                // On the other hand, if the data size is large enough, it's better to use shared
-                // memory. When data is too large, binder can't handle it.
-                if (usedSize >= MediaBuffer::kSharedMemThreshold) {
-                    ALOGV("use shared memory: %zu", usedSize);
-
-                    MediaBuffer *transferBuf = buf;
-                    size_t offset = buf->range_offset();
-                    if (transferBuf->mMemory == NULL) {
-                        if (mGroup == NULL) {
-                            mGroup = new MediaBufferGroup;
-                            size_t allocateSize = usedSize;
-                            if (usedSize < SIZE_MAX / 3) {
-                                allocateSize = usedSize * 3 / 2;
-                            }
-                            mGroup->add_buffer(new MediaBuffer(allocateSize));
-                        }
-
-                        MediaBuffer *newBuf = NULL;
-                        ret = mGroup->acquire_buffer(
-                                &newBuf, false /* nonBlocking */, usedSize);
-                        if (ret != OK || newBuf == NULL || newBuf->mMemory == NULL) {
-                            ALOGW("failed to acquire shared memory, ret %d", ret);
-                            buf->release();
-                            if (newBuf != NULL) {
-                                newBuf->release();
-                            }
-                            reply->writeInt32(NULL_BUFFER);
-                            return NO_ERROR;
-                        }
-                        transferBuf = newBuf;
-                        memcpy(transferBuf->data(), (uint8_t*)buf->data() + buf->range_offset(),
-                                buf->range_length());
-                        offset = 0;
-                    }
-
-                    reply->writeInt32(SHARED_BUFFER);
-                    RemoteMediaBufferReleaser *wrapper =
-                        new RemoteMediaBufferReleaser(transferBuf, this);
-                    reply->writeStrongBinder(wrapper);
-                    reply->writeStrongBinder(IInterface::asBinder(transferBuf->mMemory));
-                    reply->writeInt32(offset);
-                    reply->writeInt32(usedSize);
-                    buf->meta_data()->writeToParcel(*reply);
-                    if (buf->mMemory == NULL) {
-                        buf->release();
-                    }
-                } else {
-                    // buffer is small: copy it
-                    if (buf->mMemory != NULL) {
-                        ALOGV("%zu shared mem available, but only %zu used", buf->mMemory->size(), buf->range_length());
-                    }
-                    reply->writeInt32(INLINE_BUFFER);
-                    reply->writeByteArray(buf->range_length(), (uint8_t*)buf->data() + buf->range_offset());
-                    buf->meta_data()->writeToParcel(*reply);
-                    buf->release();
-                }
-            } else {
-                ALOGV("ret %d, buf %p", ret, buf);
-                reply->writeInt32(NULL_BUFFER);
-            }
-            return NO_ERROR;
-        }
         case READMULTIPLE: {
-            ALOGV("readmultiple");
+            ALOGV("readMultiple");
             CHECK_INTERFACE(IMediaSource, data, reply);
+
+            // Get max number of buffers to read.
             uint32_t maxNumBuffers;
             data.readUint32(&maxNumBuffers);
-            status_t ret = NO_ERROR;
-            uint32_t bufferCount = 0;
             if (maxNumBuffers > kMaxNumReadMultiple) {
                 maxNumBuffers = kMaxNumReadMultiple;
             }
-            while (bufferCount < maxNumBuffers) {
-                if (reply->dataSize() >= MediaBuffer::kSharedMemThreshold) {
+
+            // Get read options, if any.
+            ReadOptions opts;
+            uint32_t len;
+            const bool useOptions =
+                    data.readUint32(&len) == NO_ERROR
+                    && len == sizeof(opts)
+                    && data.read((void *)&opts, len) == NO_ERROR;
+
+            mGroup->gc(kBinderMediaBuffers /* freeBuffers */);
+            mIndexCache.gc();
+            size_t inlineTransferSize = 0;
+            status_t ret = NO_ERROR;
+            uint32_t bufferCount = 0;
+            for (; bufferCount < maxNumBuffers; ++bufferCount, ++mBuffersSinceStop) {
+                MediaBuffer *buf = nullptr;
+                ret = read(&buf, useOptions ? &opts : nullptr);
+                opts.clearNonPersistent(); // Remove options that only apply to first buffer.
+                if (ret != NO_ERROR || buf == nullptr) {
                     break;
                 }
 
-                MediaBuffer *buf = NULL;
-                ret = read(&buf, NULL);
-                if (ret != NO_ERROR || buf == NULL) {
-                    break;
+                // Even if we're using shared memory, we might not want to use it, since for small
+                // sizes it's faster to copy data through the Binder transaction
+                // On the other hand, if the data size is large enough, it's better to use shared
+                // memory. When data is too large, binder can't handle it.
+                //
+                // TODO: reduce MediaBuffer::kSharedMemThreshold
+                MediaBuffer *transferBuf = nullptr;
+                const size_t length = buf->range_length();
+                size_t offset = buf->range_offset();
+                if (length >= (supportNonblockingRead() && buf->mMemory != nullptr ?
+                        kTransferSharedAsSharedThreshold : kTransferInlineAsSharedThreshold)) {
+                    if (buf->mMemory != nullptr) {
+                        ALOGV("Use shared memory: %zu", length);
+                        transferBuf = buf;
+                    } else {
+                        ALOGD("Large buffer %zu without IMemory!", length);
+                        ret = mGroup->acquire_buffer(
+                                &transferBuf, false /* nonBlocking */, length);
+                        if (ret != OK
+                                || transferBuf == nullptr
+                                || transferBuf->mMemory == nullptr) {
+                            ALOGW("Failed to acquire shared memory, size %zu, ret %d",
+                                    length, ret);
+                            if (transferBuf != nullptr) {
+                                transferBuf->release();
+                                transferBuf = nullptr;
+                            }
+                            // Current buffer transmit inline; no more additional buffers.
+                            maxNumBuffers = 0;
+                        } else {
+                            memcpy(transferBuf->data(), (uint8_t*)buf->data() + offset, length);
+                            offset = 0;
+                            if (!mGroup->has_buffers()) {
+                                maxNumBuffers = 0; // No more MediaBuffers, stop readMultiple.
+                            }
+                        }
+                    }
                 }
-                ++bufferCount;
-                reply->writeInt32(1);  // indicate one more MediaBuffer.
-                reply->writeByteArray(
-                        buf->range_length(), (uint8_t*)buf->data() + buf->range_offset());
-                buf->meta_data()->writeToParcel(*reply);
-                buf->release();
+                if (transferBuf != nullptr) { // Using shared buffers.
+                    if (!transferBuf->isObserved()) {
+                        // Transfer buffer must be part of a MediaBufferGroup.
+                        ALOGV("adding shared memory buffer %p to local group", transferBuf);
+                        mGroup->add_buffer(transferBuf);
+                        transferBuf->add_ref(); // We have already acquired buffer.
+                    }
+                    uint64_t index = mIndexCache.lookup(transferBuf->mMemory);
+                    if (index == 0) {
+                        index = mIndexCache.insert(transferBuf->mMemory);
+                        reply->writeInt32(SHARED_BUFFER);
+                        reply->writeUint64(index);
+                        reply->writeStrongBinder(IInterface::asBinder(transferBuf->mMemory));
+                        ALOGV("SHARED_BUFFER(%p) %llu",
+                                transferBuf, (unsigned long long)index);
+                    } else {
+                        reply->writeInt32(SHARED_BUFFER_INDEX);
+                        reply->writeUint64(index);
+                        ALOGV("SHARED_BUFFER_INDEX(%p) %llu",
+                                transferBuf, (unsigned long long)index);
+                    }
+                    reply->writeInt32(offset);
+                    reply->writeInt32(length);
+                    buf->meta_data()->writeToParcel(*reply);
+                    if (transferBuf != buf) {
+                        buf->release();
+                    } else if (!supportNonblockingRead()) {
+                        maxNumBuffers = 0; // stop readMultiple with one shared buffer.
+                    }
+                } else {
+                    ALOGV_IF(buf->mMemory != nullptr,
+                            "INLINE(%p) %zu shared mem available, but only %zu used",
+                            buf, buf->mMemory->size(), length);
+                    reply->writeInt32(INLINE_BUFFER);
+                    reply->writeByteArray(length, (uint8_t*)buf->data() + offset);
+                    buf->meta_data()->writeToParcel(*reply);
+                    buf->release();
+                    inlineTransferSize += length;
+                    if (inlineTransferSize > kInlineMaxTransfer) {
+                        maxNumBuffers = 0; // stop readMultiple if inline transfer is too large.
+                    }
+                }
             }
-            reply->writeInt32(0);  // indicate no more MediaBuffer.
+            reply->writeInt32(NULL_BUFFER); // Indicate no more MediaBuffers.
             reply->writeInt32(ret);
+            ALOGV("readMultiple status %d, bufferCount %u, sinceStop %u",
+                    ret, bufferCount, mBuffersSinceStop);
+            return NO_ERROR;
+        }
+        case SUPPORT_NONBLOCKING_READ: {
+            ALOGV("supportNonblockingRead");
+            CHECK_INTERFACE(IMediaSource, data, reply);
+            reply->writeInt32((int32_t)supportNonblockingRead());
             return NO_ERROR;
         }
         default:
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 1a0539d..af2d0f3 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1385,7 +1385,7 @@
             if (mIsWidevine) {
                 maxBuffers = 2;
             } else {
-                maxBuffers = 4;
+                maxBuffers = 8;  // too large of a number may influence seeks
             }
             break;
         case MEDIA_TRACK_TYPE_AUDIO:
@@ -1417,25 +1417,24 @@
     MediaSource::ReadOptions options;
 
     bool seeking = false;
-
     if (seekTimeUs >= 0) {
         options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
         seeking = true;
     }
 
-    if (mIsWidevine) {
+    const bool couldReadMultiple = (!mIsWidevine && track->mSource->supportReadMultiple());
+
+    if (mIsWidevine || couldReadMultiple) {
         options.setNonBlocking();
     }
 
-    bool couldReadMultiple =
-        (!mIsWidevine && trackType == MEDIA_TRACK_TYPE_AUDIO
-                && track->mSource->supportReadMultiple());
     for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
         Vector<MediaBuffer *> mediaBuffers;
         status_t err = NO_ERROR;
 
-        if (!seeking && couldReadMultiple) {
-            err = track->mSource->readMultiple(&mediaBuffers, (maxBuffers - numBuffers));
+        if (couldReadMultiple) {
+            err = track->mSource->readMultiple(
+                    &mediaBuffers, maxBuffers - numBuffers, &options);
         } else {
             MediaBuffer *mbuf = NULL;
             err = track->mSource->read(&mbuf, &options);
@@ -1444,7 +1443,7 @@
             }
         }
 
-        options.clearSeekTo();
+        options.clearNonPersistent();
 
         size_t id = 0;
         size_t count = mediaBuffers.size();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
index 2de829b..0c6f652 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -24,7 +24,7 @@
 
 namespace android {
 
-struct MemoryDealer;
+class MemoryDealer;
 
 struct NuPlayer::NuPlayerStreamListener : public BnStreamListener {
     NuPlayerStreamListener(
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 1b7dff5..8a305de 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -318,10 +318,16 @@
     size_t numTracks = mTracks.size();
     size_t preparedCount, underflowCount, overflowCount, startCount;
     preparedCount = underflowCount = overflowCount = startCount = 0;
-    for (size_t i = 0; i < numTracks; ++i) {
+
+    size_t count = numTracks;
+    for (size_t i = 0; i < count; ++i) {
         status_t finalResult;
         TrackInfo *info = &mTracks.editItemAt(i);
         sp<AnotherPacketSource> src = info->mSource;
+        if (src == NULL) {
+            --numTracks;
+            continue;
+        }
         int64_t bufferedDurationUs = src->getBufferedDurationUs(&finalResult);
 
         // isFinished when duration is 0 checks for EOS result only
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 9bb4b39..3dea270 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -3872,7 +3872,7 @@
 
         case OMX_VIDEO_CodingVP8:
         case OMX_VIDEO_CodingVP9:
-            err = setupVPXEncoderParameters(msg);
+            err = setupVPXEncoderParameters(msg, outputFormat);
             break;
 
         default:
@@ -3969,7 +3969,7 @@
 }
 
 static OMX_U32 setPFramesSpacing(
-        int32_t iFramesInterval /* seconds */, int32_t frameRate, uint32_t BFramesSpacing = 0) {
+        float iFramesInterval /* seconds */, int32_t frameRate, uint32_t BFramesSpacing = 0) {
     // BFramesSpacing is the number of B frames between I/P frames
     // PFramesSpacing (the value to be returned) is the number of P frames between I frames
     //
@@ -4005,9 +4005,10 @@
 }
 
 status_t ACodec::setupMPEG4EncoderParameters(const sp<AMessage> &msg) {
-    int32_t bitrate, iFrameInterval;
+    int32_t bitrate;
+    float iFrameInterval;
     if (!msg->findInt32("bitrate", &bitrate)
-            || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+            || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
         return INVALID_OPERATION;
     }
 
@@ -4086,9 +4087,10 @@
 }
 
 status_t ACodec::setupH263EncoderParameters(const sp<AMessage> &msg) {
-    int32_t bitrate, iFrameInterval;
+    int32_t bitrate;
+    float iFrameInterval;
     if (!msg->findInt32("bitrate", &bitrate)
-            || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+            || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
         return INVALID_OPERATION;
     }
 
@@ -4214,9 +4216,10 @@
 }
 
 status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) {
-    int32_t bitrate, iFrameInterval;
+    int32_t bitrate;
+    float iFrameInterval;
     if (!msg->findInt32("bitrate", &bitrate)
-            || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+            || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
         return INVALID_OPERATION;
     }
 
@@ -4373,9 +4376,10 @@
 }
 
 status_t ACodec::setupHEVCEncoderParameters(const sp<AMessage> &msg) {
-    int32_t bitrate, iFrameInterval;
+    int32_t bitrate;
+    float iFrameInterval;
     if (!msg->findInt32("bitrate", &bitrate)
-            || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+            || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
         return INVALID_OPERATION;
     }
 
@@ -4428,9 +4432,9 @@
     return configureBitrate(bitrate, bitrateMode);
 }
 
-status_t ACodec::setupVPXEncoderParameters(const sp<AMessage> &msg) {
+status_t ACodec::setupVPXEncoderParameters(const sp<AMessage> &msg, sp<AMessage> &outputFormat) {
     int32_t bitrate;
-    int32_t iFrameInterval = 0;
+    float iFrameInterval = 0;
     size_t tsLayers = 0;
     OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE pattern =
         OMX_VIDEO_VPXTemporalLayerPatternNone;
@@ -4444,7 +4448,7 @@
     if (!msg->findInt32("bitrate", &bitrate)) {
         return INVALID_OPERATION;
     }
-    msg->findInt32("i-frame-interval", &iFrameInterval);
+    msg->findAsFloat("i-frame-interval", &iFrameInterval);
 
     OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
 
@@ -4458,6 +4462,9 @@
     }
 
     AString tsSchema;
+    OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE tsType =
+        OMX_VIDEO_AndroidTemporalLayeringPatternNone;
+
     if (msg->findString("ts-schema", &tsSchema)) {
         unsigned int numLayers = 0;
         unsigned int numBLayers = 0;
@@ -4466,6 +4473,7 @@
         if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &dummy) == 1
                 && numLayers > 0) {
             pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
+            tsType = OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC;
             tsLayers = numLayers;
         } else if ((tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
                         &numLayers, &dummy, &numBLayers, &dummy))
@@ -4473,6 +4481,7 @@
                 && numLayers > 0 && numLayers < UINT32_MAX - numBLayers) {
             pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
             // VPX does not have a concept of B-frames, so just count all layers
+            tsType = OMX_VIDEO_AndroidTemporalLayeringPatternAndroid;
             tsLayers = numLayers + numBLayers;
         } else {
             ALOGW("Ignoring unsupported ts-schema [%s]", tsSchema.c_str());
@@ -4509,6 +4518,12 @@
                 &vp8type, sizeof(vp8type));
         if (err != OK) {
             ALOGW("Extended VP8 parameters set failed: %d", err);
+        } else if (tsType == OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC) {
+            // advertise even single layer WebRTC layering, as it is defined
+            outputFormat->setString("ts-schema", AStringPrintf("webrtc.vp8.%u-layer", tsLayers));
+        } else if (tsLayers > 0) {
+            // tsType == OMX_VIDEO_AndroidTemporalLayeringPatternAndroid
+            outputFormat->setString("ts-schema", AStringPrintf("android.generic.%u", tsLayers));
         }
     }
 
@@ -5039,32 +5054,21 @@
                             sizeof(vp8type));
 
                     if (err == OK) {
-                        AString tsSchema = "none";
-                        if (vp8type.eTemporalPattern
-                                == OMX_VIDEO_VPXTemporalLayerPatternWebRTC) {
-                            switch (vp8type.nTemporalLayerCount) {
-                                case 1:
-                                {
-                                    tsSchema = "webrtc.vp8.1-layer";
-                                    break;
-                                }
-                                case 2:
-                                {
-                                    tsSchema = "webrtc.vp8.2-layer";
-                                    break;
-                                }
-                                case 3:
-                                {
-                                    tsSchema = "webrtc.vp8.3-layer";
-                                    break;
-                                }
-                                default:
-                                {
-                                    break;
-                                }
+                        if (vp8type.eTemporalPattern == OMX_VIDEO_VPXTemporalLayerPatternWebRTC
+                                && vp8type.nTemporalLayerCount > 0
+                                && vp8type.nTemporalLayerCount
+                                        <= OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS) {
+                            // advertise as android.generic if we configured for android.generic
+                            AString origSchema;
+                            if (notify->findString("ts-schema", &origSchema)
+                                    && origSchema.startsWith("android.generic")) {
+                                notify->setString("ts-schema", AStringPrintf(
+                                        "android.generic.%u", vp8type.nTemporalLayerCount));
+                            } else {
+                                notify->setString("ts-schema", AStringPrintf(
+                                        "webrtc.vp8.%u-layer", vp8type.nTemporalLayerCount));
                             }
                         }
-                        notify->setString("ts-schema", tsSchema);
                     }
                     // Fall through to set up mime.
                 }
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 58448010..bbc75d6 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -75,6 +75,7 @@
     virtual sp<MetaData> getFormat();
 
     virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
+    virtual bool supportNonblockingRead() { return true; }
     virtual status_t fragmentedRead(MediaBuffer **buffer, const ReadOptions *options = NULL);
 
 protected:
@@ -3563,13 +3564,20 @@
 
     // A somewhat arbitrary limit that should be sufficient for 8k video frames
     // If you see the message below for a valid input stream: increase the limit
-    if (max_size > 64 * 1024 * 1024) {
-        ALOGE("bogus max input size: %zu", max_size);
+    const size_t kMaxBufferSize = 64 * 1024 * 1024;
+    if (max_size > kMaxBufferSize) {
+        ALOGE("bogus max input size: %zu > %zu", max_size, kMaxBufferSize);
         return ERROR_MALFORMED;
     }
-    mGroup = new MediaBufferGroup;
-    mGroup->add_buffer(new MediaBuffer(max_size));
+    if (max_size == 0) {
+        ALOGE("zero max input size");
+        return ERROR_MALFORMED;
+    }
 
+    // Allow up to kMaxBuffers, but not if the total exceeds kMaxBufferSize.
+    const size_t kMaxBuffers = 8;
+    const size_t buffers = min(kMaxBufferSize / max_size, kMaxBuffers);
+    mGroup = new MediaBufferGroup(buffers, max_size);
     mSrcBuffer = new (std::nothrow) uint8_t[max_size];
     if (mSrcBuffer == NULL) {
         // file probably specified a bad max size
@@ -4196,6 +4204,11 @@
 
     CHECK(mStarted);
 
+    if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) {
+        *out = nullptr;
+        return WOULD_BLOCK;
+    }
+
     if (mFirstMoofOffset > 0) {
         return fragmentedRead(out, options);
     }
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 38a2a06..1f04fa0 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -70,6 +70,8 @@
     virtual status_t read(
             MediaBuffer **buffer, const ReadOptions *options = NULL);
 
+    virtual bool supportNonblockingRead() { return true; }
+
 protected:
     virtual ~WAVSource();
 
@@ -377,8 +379,8 @@
 
     CHECK(!mStarted);
 
-    mGroup = new MediaBufferGroup;
-    mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+    // some WAV files may have large audio buffers that use shared memory transfer.
+    mGroup = new MediaBufferGroup(4 /* buffers */, kMaxFrameSize);
 
     if (mBitsPerSample == 8) {
         // As a temporary buffer for 8->16 bit conversion.
@@ -415,6 +417,10 @@
         MediaBuffer **out, const ReadOptions *options) {
     *out = NULL;
 
+    if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) {
+        return WOULD_BLOCK;
+    }
+
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
     if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
index 6ec8c41..9e7a3be 100644
--- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
@@ -1016,10 +1016,10 @@
 
             if ((avcType->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) &&
                     avcType->nPFrames) {
-                mBframes = avcType->nBFrames / avcType->nPFrames;
+                mBframes = avcType->nBFrames;
             }
 
-            mIInterval = avcType->nPFrames + avcType->nBFrames;
+            mIInterval = (avcType->nPFrames + 1) * (avcType->nBFrames + 1);
             mConstrainedIntraFlag = avcType->bconstIpred;
 
             if (OMX_VIDEO_AVCLoopFilterDisable == avcType->eLoopFilterMode)
@@ -1033,7 +1033,9 @@
                     || avcType->bDirect8x8Inference != OMX_FALSE
                     || avcType->bDirectSpatialTemporal != OMX_FALSE
                     || avcType->nCabacInitIdc != 0) {
-                return OMX_ErrorUndefined;
+                // OMX does not allow a way to signal what values are wrong, so it's
+                // best for components to just do best effort in supporting these values
+                ALOGV("ignoring unsupported settings");
             }
 
             if (OK != ConvertOmxAvcLevelToAvcSpecLevel(avcType->eLevel, &mAVCEncLevel)) {
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
index 7638bb7..8802fad 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
@@ -76,7 +76,7 @@
             176 /* width */, 144 /* height */,
             callbacks, appData, component),
       mEncodeMode(COMBINE_MODE_WITH_ERR_RES),
-      mIDRFrameRefreshIntervalInSec(1),
+      mKeyFrameInterval(30),
       mNumInputFrames(-1),
       mStarted(false),
       mSawInputEOS(false),
@@ -159,14 +159,7 @@
     }
 
     // Set IDR frame refresh interval
-    if (mIDRFrameRefreshIntervalInSec < 0) {
-        mEncParams->intraPeriod = -1;
-    } else if (mIDRFrameRefreshIntervalInSec == 0) {
-        mEncParams->intraPeriod = 1;  // All I frames
-    } else {
-        mEncParams->intraPeriod =
-            (mIDRFrameRefreshIntervalInSec * mFramerate) >> 16;
-    }
+    mEncParams->intraPeriod = mKeyFrameInterval;
 
     mEncParams->numIntraMB = 0;
     mEncParams->sceneDetect = PV_ON;
@@ -378,6 +371,8 @@
                 return OMX_ErrorUndefined;
             }
 
+            mKeyFrameInterval = int32_t(mpeg4type->nPFrames + 1);
+
             return OMX_ErrorNone;
         }
 
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
index 3389c37..bb6ea92 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
@@ -63,7 +63,7 @@
     } InputBufferInfo;
 
     MP4EncodingMode mEncodeMode;
-    int32_t  mIDRFrameRefreshIntervalInSec;
+    int32_t  mKeyFrameInterval; // 1: all I-frames, <0: infinite
 
     int64_t  mNumInputFrames;
     bool     mStarted;
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 37fb33f..a4583d6 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -212,6 +212,33 @@
     return NULL;
 }
 
+bool AMessage::findAsFloat(const char *name, float *value) const {
+    size_t i = findItemIndex(name, strlen(name));
+    if (i < mNumItems) {
+        const Item *item = &mItems[i];
+        switch (item->mType) {
+            case kTypeFloat:
+                *value = item->u.floatValue;
+                return true;
+            case kTypeDouble:
+                *value = (float)item->u.doubleValue;
+                return true;
+            case kTypeInt64:
+                *value = (float)item->u.int64Value;
+                return true;
+            case kTypeInt32:
+                *value = (float)item->u.int32Value;
+                return true;
+            case kTypeSize:
+                *value = (float)item->u.sizeValue;
+                return true;
+            default:
+                return false;
+        }
+    }
+    return false;
+}
+
 bool AMessage::contains(const char *name) const {
     size_t i = findItemIndex(name, strlen(name));
     return i < mNumItems;
diff --git a/media/libstagefright/foundation/MediaBuffer.cpp b/media/libstagefright/foundation/MediaBuffer.cpp
index fa8e241..15d557d 100644
--- a/media/libstagefright/foundation/MediaBuffer.cpp
+++ b/media/libstagefright/foundation/MediaBuffer.cpp
@@ -32,7 +32,6 @@
 
 MediaBuffer::MediaBuffer(void *data, size_t size)
     : mObserver(NULL),
-      mNextBuffer(NULL),
       mRefCount(0),
       mData(data),
       mSize(size),
@@ -45,7 +44,6 @@
 
 MediaBuffer::MediaBuffer(size_t size)
     : mObserver(NULL),
-      mNextBuffer(NULL),
       mRefCount(0),
       mData(NULL),
       mSize(size),
@@ -57,8 +55,10 @@
     if (size < kSharedMemThreshold) {
         mData = malloc(size);
     } else {
-        sp<MemoryDealer> memoryDealer = new MemoryDealer(size, "MediaBuffer");
-        mMemory = memoryDealer->allocate(size);
+        ALOGV("creating memoryDealer");
+        sp<MemoryDealer> memoryDealer =
+                new MemoryDealer(size + sizeof(SharedControl), "MediaBuffer");
+        mMemory = memoryDealer->allocate(size + sizeof(SharedControl));
         if (mMemory == NULL) {
             ALOGW("Failed to allocate shared memory, trying regular allocation!");
             mData = malloc(size);
@@ -66,7 +66,8 @@
                 ALOGE("Out of memory");
             }
         } else {
-            mData = mMemory->pointer();
+            getSharedControl()->clear();
+            mData = (uint8_t *)mMemory->pointer() + sizeof(SharedControl);
             ALOGV("Allocated shared mem buffer of size %zu @ %p", size, mData);
         }
     }
@@ -74,7 +75,6 @@
 
 MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer)
     : mObserver(NULL),
-      mNextBuffer(NULL),
       mRefCount(0),
       mData(NULL),
       mSize(1),
@@ -88,7 +88,6 @@
 
 MediaBuffer::MediaBuffer(const sp<ABuffer> &buffer)
     : mObserver(NULL),
-      mNextBuffer(NULL),
       mRefCount(0),
       mData(buffer->data()),
       mSize(buffer->size()),
@@ -102,6 +101,14 @@
 
 void MediaBuffer::release() {
     if (mObserver == NULL) {
+        if (mMemory.get() != nullptr) {
+            // See if there is a pending release and there are no observers.
+            // Ideally this never happens.
+            while (addPendingRelease(-1) > 0) {
+                __sync_fetch_and_sub(&mRefCount, 1);
+            }
+            addPendingRelease(1);
+        }
         CHECK_EQ(mRefCount, 0);
         delete this;
         return;
@@ -183,6 +190,10 @@
         mOriginal->release();
         mOriginal = NULL;
     }
+
+   if (mMemory.get() != nullptr) {
+       getSharedControl()->setDeadObject();
+   }
 }
 
 void MediaBuffer::setObserver(MediaBufferObserver *observer) {
@@ -190,14 +201,6 @@
     mObserver = observer;
 }
 
-void MediaBuffer::setNextBuffer(MediaBuffer *buffer) {
-    mNextBuffer = buffer;
-}
-
-MediaBuffer *MediaBuffer::nextBuffer() {
-    return mNextBuffer;
-}
-
 int MediaBuffer::refcount() const {
     return mRefCount;
 }
diff --git a/media/libstagefright/foundation/MediaBufferGroup.cpp b/media/libstagefright/foundation/MediaBufferGroup.cpp
index 9022324..cb78879 100644
--- a/media/libstagefright/foundation/MediaBufferGroup.cpp
+++ b/media/libstagefright/foundation/MediaBufferGroup.cpp
@@ -23,20 +23,65 @@
 
 namespace android {
 
-MediaBufferGroup::MediaBufferGroup()
-    : mFirstBuffer(NULL),
-      mLastBuffer(NULL) {
+// std::min is not constexpr in C++11
+template<typename T>
+constexpr T MIN(const T &a, const T &b) { return a <= b ? a : b; }
+
+// MediaBufferGroup may create shared memory buffers at a
+// smaller threshold than an isolated new MediaBuffer.
+static const size_t kSharedMemoryThreshold = MIN(
+        (size_t)MediaBuffer::kSharedMemThreshold, (size_t)(4 * 1024));
+
+MediaBufferGroup::MediaBufferGroup(size_t growthLimit) :
+    mGrowthLimit(growthLimit) {
+}
+
+MediaBufferGroup::MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit)
+    : mGrowthLimit(growthLimit) {
+
+    if (buffer_size >= kSharedMemoryThreshold) {
+        ALOGD("creating MemoryDealer");
+        // Using a single MemoryDealer is efficient for a group of shared memory objects.
+        // This loop guarantees that we use shared memory (no fallback to malloc).
+
+        size_t alignment = MemoryDealer::getAllocationAlignment();
+        size_t augmented_size = buffer_size + sizeof(MediaBuffer::SharedControl);
+        size_t total = (augmented_size + alignment - 1) / alignment * alignment * buffers;
+        sp<MemoryDealer> memoryDealer = new MemoryDealer(total, "MediaBufferGroup");
+
+        for (size_t i = 0; i < buffers; ++i) {
+            sp<IMemory> mem = memoryDealer->allocate(augmented_size);
+            if (mem.get() == nullptr) {
+                ALOGW("Only allocated %zu shared buffers of size %zu", i, buffer_size);
+                break;
+            }
+            MediaBuffer *buffer = new MediaBuffer(mem);
+            buffer->getSharedControl()->clear();
+            add_buffer(buffer);
+        }
+        return;
+    }
+
+    // Non-shared memory allocation.
+    for (size_t i = 0; i < buffers; ++i) {
+        MediaBuffer *buffer = new MediaBuffer(buffer_size);
+        if (buffer->data() == nullptr) {
+            delete buffer; // don't call release, it's not properly formed
+            ALOGW("Only allocated %zu malloc buffers of size %zu", i, buffer_size);
+            break;
+        }
+        add_buffer(buffer);
+    }
 }
 
 MediaBufferGroup::~MediaBufferGroup() {
-    MediaBuffer *next;
-    for (MediaBuffer *buffer = mFirstBuffer; buffer != NULL;
-         buffer = next) {
-        next = buffer->nextBuffer();
-
-        CHECK_EQ(buffer->refcount(), 0);
-
-        buffer->setObserver(NULL);
+    for (MediaBuffer *buffer : mBuffers) {
+        buffer->resolvePendingRelease();
+        // If we don't release it, perhaps noone will release it.
+        LOG_ALWAYS_FATAL_IF(buffer->refcount() != 0,
+                "buffer refcount %p = %d != 0", buffer, buffer->refcount());
+        // actually delete it.
+        buffer->setObserver(nullptr);
         buffer->release();
     }
 }
@@ -45,87 +90,105 @@
     Mutex::Autolock autoLock(mLock);
 
     buffer->setObserver(this);
+    mBuffers.emplace_back(buffer);
+    // optionally: mGrowthLimit = max(mGrowthLimit, mBuffers.size());
+}
 
-    if (mLastBuffer) {
-        mLastBuffer->setNextBuffer(buffer);
-    } else {
-        mFirstBuffer = buffer;
+void MediaBufferGroup::gc(size_t freeBuffers) {
+    Mutex::Autolock autoLock(mLock);
+
+    size_t freeCount = 0;
+    for (auto it = mBuffers.begin(); it != mBuffers.end(); ) {
+        (*it)->resolvePendingRelease();
+        if ((*it)->isDeadObject()) {
+            // The MediaBuffer has been deleted, why is it in the MediaBufferGroup?
+            LOG_ALWAYS_FATAL("buffer(%p) has dead object with refcount %d",
+                    (*it), (*it)->refcount());
+        } else if ((*it)->refcount() == 0 && ++freeCount > freeBuffers) {
+            (*it)->setObserver(nullptr);
+            (*it)->release();
+            it = mBuffers.erase(it);
+        } else {
+            ++it;
+        }
     }
+}
 
-    mLastBuffer = buffer;
+bool MediaBufferGroup::has_buffers() {
+    if (mBuffers.size() < mGrowthLimit) {
+        return true; // We can add more buffers internally.
+    }
+    for (MediaBuffer *buffer : mBuffers) {
+        buffer->resolvePendingRelease();
+        if (buffer->refcount() == 0) {
+            return true;
+        }
+    }
+    return false;
 }
 
 status_t MediaBufferGroup::acquire_buffer(
         MediaBuffer **out, bool nonBlocking, size_t requestedSize) {
     Mutex::Autolock autoLock(mLock);
-
     for (;;) {
-        MediaBuffer *freeBuffer = NULL;
-        MediaBuffer *freeBufferPrevious = NULL;
-        MediaBuffer *buffer = NULL;
-        MediaBuffer *bufferPrevious = NULL;
         size_t smallest = requestedSize;
-        for (buffer = mFirstBuffer;
-             buffer != NULL; buffer = buffer->nextBuffer()) {
-            if (buffer->refcount() == 0) {
-               if (buffer->size() >= requestedSize) {
-                   break;
-               } else if (buffer->size() < smallest) {
-                   freeBuffer = buffer;
-                   freeBufferPrevious = bufferPrevious;
-               }
+        MediaBuffer *buffer = nullptr;
+        auto free = mBuffers.end();
+        for (auto it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+            (*it)->resolvePendingRelease();
+            if ((*it)->refcount() == 0) {
+                const size_t size = (*it)->size();
+                if (size >= requestedSize) {
+                    buffer = *it;
+                    break;
+                }
+                if (size < smallest) {
+                    smallest = size; // always free the smallest buf
+                    free = it;
+                }
             }
-            bufferPrevious = buffer;
         }
-
-        if (buffer == NULL && freeBuffer != NULL) {
-            ALOGV("allocate new buffer, requested size %zu vs available %zu",
-                    requestedSize, freeBuffer->size());
-            size_t allocateSize = requestedSize;
-            if (requestedSize < SIZE_MAX / 3) {
-                allocateSize = requestedSize * 3 / 2;
+        if (buffer == nullptr
+                && (free != mBuffers.end() || mBuffers.size() < mGrowthLimit)) {
+            // We alloc before we free so failure leaves group unchanged.
+            const size_t allocateSize = requestedSize < SIZE_MAX / 3 * 2 /* NB: ordering */ ?
+                    requestedSize * 3 / 2 : requestedSize;
+            buffer = new MediaBuffer(allocateSize);
+            if (buffer->data() == nullptr) {
+                ALOGE("Allocation failure for size %zu", allocateSize);
+                delete buffer; // Invalid alloc, prefer not to call release.
+                buffer = nullptr;
+            } else {
+                buffer->setObserver(this);
+                if (free != mBuffers.end()) {
+                    ALOGV("reallocate buffer, requested size %zu vs available %zu",
+                            requestedSize, (*free)->size());
+                    (*free)->setObserver(nullptr);
+                    (*free)->release();
+                    *free = buffer; // in-place replace
+                } else {
+                    ALOGV("allocate buffer, requested size %zu", requestedSize);
+                    mBuffers.emplace_back(buffer);
+                }
             }
-            MediaBuffer *newBuffer = new MediaBuffer(allocateSize);
-            newBuffer->setObserver(this);
-            if (freeBuffer == mFirstBuffer) {
-                mFirstBuffer = newBuffer;
-            }
-            if (freeBuffer == mLastBuffer) {
-                mLastBuffer = newBuffer;
-            }
-            newBuffer->setNextBuffer(freeBuffer->nextBuffer());
-            if (freeBufferPrevious != NULL) {
-                freeBufferPrevious->setNextBuffer(newBuffer);
-            }
-            freeBuffer->setObserver(NULL);
-            freeBuffer->release();
-
-            buffer = newBuffer;
         }
-
-        if (buffer != NULL) {
+        if (buffer != nullptr) {
             buffer->add_ref();
             buffer->reset();
-
             *out = buffer;
-            goto exit;
+            return OK;
         }
-
         if (nonBlocking) {
-            *out = NULL;
+            *out = nullptr;
             return WOULD_BLOCK;
         }
-
-        // All buffers are in use. Block until one of them is returned to us.
+        // All buffers are in use, block until one of them is returned.
         mCondition.wait(mLock);
     }
-
-exit:
-    return OK;
+    // Never gets here.
 }
 
 void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
-    Mutex::Autolock autoLock(mLock);
     mCondition.signal();
 }
 
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 47573c3..8b0331a 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -214,11 +214,13 @@
 
     char key[32];
     snprintf(key, sizeof(key), "a=rtpmap:%lu", x);
-
-    CHECK(findAttribute(index, key, desc));
-
-    snprintf(key, sizeof(key), "a=fmtp:%lu", x);
-    if (!findAttribute(index, key, params)) {
+    if (findAttribute(index, key, desc)) {
+        snprintf(key, sizeof(key), "a=fmtp:%lu", x);
+        if (!findAttribute(index, key, params)) {
+            params->clear();
+        }
+    } else {
+        desc->clear();
         params->clear();
     }
 }
diff --git a/media/mediaserver/mediaserver.rc b/media/mediaserver/mediaserver.rc
index b777d5c..faab938 100644
--- a/media/mediaserver/mediaserver.rc
+++ b/media/mediaserver/mediaserver.rc
@@ -4,3 +4,4 @@
     group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
     ioprio rt 4
     writepid /dev/cpuset/foreground/tasks
+    writepid /dev/stune/foreground/tasks
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index d202169..873a9ad 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -31,7 +31,7 @@
 
 /*static*/ const FastCaptureState FastCapture::sInitial;
 
-FastCapture::FastCapture() : FastThread(),
+FastCapture::FastCapture() : FastThread("cycleC_ms", "loadC_us"),
     mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0),
     mReadBuffer(NULL), mReadBufferState(-1), mFormat(Format_Invalid), mSampleRate(0),
     // mDummyDumpState
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 01f3939..b0780a4 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -46,7 +46,7 @@
 
 /*static*/ const FastMixerState FastMixer::sInitial;
 
-FastMixer::FastMixer() : FastThread(),
+FastMixer::FastMixer() : FastThread("cycle_ms", "load_us"),
     // mFastTrackNames
     // mGenerations
     mOutputSink(NULL),
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index 5ca579b..8da54b0 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -35,7 +35,7 @@
 
 namespace android {
 
-FastThread::FastThread() : Thread(false /*canCallJava*/),
+FastThread::FastThread(const char *cycleMs, const char *loadUs) : Thread(false /*canCallJava*/),
     // re-initialized to &sInitial by subclass constructor
     mPrevious(NULL), mCurrent(NULL),
     /* mOldTs({0, 0}), */
@@ -72,11 +72,15 @@
     frameCount(0),
 #endif
     mAttemptedWrite(false)
+    // mCycleMs(cycleMs)
+    // mLoadUs(loadUs)
 {
     mOldTs.tv_sec = 0;
     mOldTs.tv_nsec = 0;
     mMeasuredWarmupTs.tv_sec = 0;
     mMeasuredWarmupTs.tv_nsec = 0;
+    strlcpy(mCycleMs, cycleMs, sizeof(mCycleMs));
+    strlcpy(mLoadUs, loadUs, sizeof(mLoadUs));
 }
 
 FastThread::~FastThread()
@@ -336,8 +340,8 @@
                     // this store #4 is not atomic with respect to stores #1, #2, #3 above, but
                     // the newest open & oldest closed halves are atomic with respect to each other
                     mDumpState->mBounds = mBounds;
-                    ATRACE_INT("cycle_ms", monotonicNs / 1000000);
-                    ATRACE_INT("load_us", loadNs / 1000);
+                    ATRACE_INT(mCycleMs, monotonicNs / 1000000);
+                    ATRACE_INT(mLoadUs, loadNs / 1000);
                 }
 #endif
             } else {
diff --git a/services/audioflinger/FastThread.h b/services/audioflinger/FastThread.h
index 2efb6de..816b666 100644
--- a/services/audioflinger/FastThread.h
+++ b/services/audioflinger/FastThread.h
@@ -30,7 +30,7 @@
 class FastThread : public Thread {
 
 public:
-            FastThread();
+            FastThread(const char *cycleMs, const char *loadUs);
     virtual ~FastThread();
 
 private:
@@ -88,6 +88,9 @@
     FastThreadState::Command mCommand;
     bool            mAttemptedWrite;
 
+    char            mCycleMs[16];   // cycle_ms + suffix
+    char            mLoadUs[16];    // load_us + suffix
+
 };  // class FastThread
 
 }   // android
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index b9f08d3..9169941 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -6045,7 +6045,7 @@
 
             // sleep with mutex unlocked
             if (sleepUs > 0) {
-                ATRACE_BEGIN("sleep");
+                ATRACE_BEGIN("sleepC");
                 mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)sleepUs));
                 ATRACE_END();
                 sleepUs = 0;