Merge "stagefright: add adaptive playback support to SoftHEVC decoder." into lmp-dev
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index f98002d..4932d40 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -449,6 +449,7 @@
     sp<EffectClient>        mIEffectClient;     // IEffectClient implementation
     sp<IMemory>             mCblkMemory;        // shared memory for deferred parameter setting
     effect_param_cblk_t*    mCblk;              // control block for deferred parameter setting
+    pid_t                   mClientPid;
 };
 
 
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
index 3d79596..c8a50e8 100644
--- a/include/media/stagefright/MediaBuffer.h
+++ b/include/media/stagefright/MediaBuffer.h
@@ -18,6 +18,8 @@
 
 #define MEDIA_BUFFER_H_
 
+#include <media/stagefright/foundation/MediaBufferBase.h>
+
 #include <pthread.h>
 
 #include <utils/Errors.h>
@@ -43,7 +45,7 @@
     MediaBufferObserver &operator=(const MediaBufferObserver &);
 };
 
-class MediaBuffer {
+class MediaBuffer : public MediaBufferBase {
 public:
     // The underlying data remains the responsibility of the caller!
     MediaBuffer(void *data, size_t size);
@@ -56,10 +58,10 @@
 
     // Decrements the reference count and returns the buffer to its
     // associated MediaBufferGroup if the reference count drops to 0.
-    void release();
+    virtual void release();
 
     // Increments the reference count.
-    void add_ref();
+    virtual void add_ref();
 
     void *data() const;
     size_t size() const;
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index b87a09e..bca78b9 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -268,7 +268,7 @@
 
     static void PostReplyWithError(int32_t replyID, int32_t err);
 
-    status_t init(const char *name, bool nameIsType, bool encoder);
+    status_t init(const AString &name, bool nameIsType, bool encoder);
 
     void setState(State newState);
     void returnBuffersToCodec();
diff --git a/include/media/stagefright/foundation/ABuffer.h b/include/media/stagefright/foundation/ABuffer.h
index 602f7ab..6294ee7 100644
--- a/include/media/stagefright/foundation/ABuffer.h
+++ b/include/media/stagefright/foundation/ABuffer.h
@@ -27,6 +27,7 @@
 namespace android {
 
 struct AMessage;
+class MediaBufferBase;
 
 struct ABuffer : public RefBase {
     ABuffer(size_t capacity);
@@ -50,6 +51,9 @@
 
     sp<AMessage> meta();
 
+    MediaBufferBase *getMediaBufferBase();
+    void setMediaBufferBase(MediaBufferBase *mediaBuffer);
+
 protected:
     virtual ~ABuffer();
 
@@ -57,6 +61,8 @@
     sp<AMessage> mFarewell;
     sp<AMessage> mMeta;
 
+    MediaBufferBase *mMediaBufferBase;
+
     void *mData;
     size_t mCapacity;
     size_t mRangeOffset;
diff --git a/include/media/stagefright/foundation/MediaBufferBase.h b/include/media/stagefright/foundation/MediaBufferBase.h
new file mode 100644
index 0000000..99418fb
--- /dev/null
+++ b/include/media/stagefright/foundation/MediaBufferBase.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 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 MEDIA_BUFFER_BASE_H_
+
+#define MEDIA_BUFFER_BASE_H_
+
+namespace android {
+
+class MediaBufferBase {
+public:
+    MediaBufferBase() {}
+
+    virtual void release() = 0;
+    virtual void add_ref() = 0;
+
+protected:
+    virtual ~MediaBufferBase() {}
+
+private:
+    MediaBufferBase(const MediaBufferBase &);
+    MediaBufferBase &operator=(const MediaBufferBase &);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_BUFFER_BASE_H_
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 35f6557..0d5d7e4 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -145,15 +145,19 @@
         return mStatus;
     }
 
-    mIEffect = iEffect;
     mCblkMemory = cblk;
     mCblk = static_cast<effect_param_cblk_t*>(cblk->pointer());
     int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
     mCblk->buffer = (uint8_t *)mCblk + bufOffset;
 
     iEffect->asBinder()->linkToDeath(mIEffectClient);
-    ALOGV("set() %p OK effect: %s id: %d status %d enabled %d", this, mDescriptor.name, mId,
-            mStatus, mEnabled);
+    mClientPid = IPCThreadState::self()->getCallingPid();
+    ALOGV("set() %p OK effect: %s id: %d status %d enabled %d pid %d", this, mDescriptor.name, mId,
+            mStatus, mEnabled, mClientPid);
+
+    if (mSessionId > AUDIO_SESSION_OUTPUT_MIX) {
+        AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
+    }
 
     return mStatus;
 }
@@ -164,6 +168,9 @@
     ALOGV("Destructor %p", this);
 
     if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) {
+        if (mSessionId > AUDIO_SESSION_OUTPUT_MIX) {
+            AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
+        }
         if (mIEffect != NULL) {
             mIEffect->disconnect();
             mIEffect->asBinder()->unlinkToDeath(mIEffectClient);
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 762dca5..ea7b279 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -1935,7 +1935,7 @@
     // To avoid a race, read the presented frames first.  This ensures that presented <= consumed.
     status_t status = mAudioTrack->getTimestamp(timestamp);
     if (status != NO_ERROR) {
-        ALOGW_IF(status != WOULD_BLOCK, "getTimestamp error:%#x", status);
+        ALOGV_IF(status != WOULD_BLOCK, "getTimestamp error:%#x", status);
         return status;
     }
     if (isOffloadedOrDirect_l()) {
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index adc066d..2cf5710 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -22,6 +22,7 @@
     StagefrightPlayer.cpp       \
     StagefrightRecorder.cpp     \
     TestPlayerStub.cpp          \
+    VideoFrameScheduler.cpp     \
 
 LOCAL_SHARED_LIBRARIES :=       \
     libbinder                   \
diff --git a/media/libmediaplayerservice/VideoFrameScheduler.cpp b/media/libmediaplayerservice/VideoFrameScheduler.cpp
new file mode 100644
index 0000000..4251c4e
--- /dev/null
+++ b/media/libmediaplayerservice/VideoFrameScheduler.cpp
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2014 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 "VideoFrameScheduler"
+#include <utils/Log.h>
+#define ATRACE_TAG ATRACE_TAG_VIDEO
+#include <utils/Trace.h>
+
+#include <sys/time.h>
+
+#include <binder/IServiceManager.h>
+#include <gui/ISurfaceComposer.h>
+#include <ui/DisplayStatInfo.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+
+#include "VideoFrameScheduler.h"
+
+namespace android {
+
+static const nsecs_t kNanosIn1s = 1000000000;
+
+template<class T>
+inline static const T divRound(const T &nom, const T &den) {
+    if ((nom >= 0) ^ (den >= 0)) {
+        return (nom - den / 2) / den;
+    } else {
+        return (nom + den / 2) / den;
+    }
+}
+
+template<class T>
+inline static T abs(const T &a) {
+    return a < 0 ? -a : a;
+}
+
+template<class T>
+inline static const T &min(const T &a, const T &b) {
+    return a < b ? a : b;
+}
+
+template<class T>
+inline static const T &max(const T &a, const T &b) {
+    return a > b ? a : b;
+}
+
+template<class T>
+inline static T periodicError(const T &val, const T &period) {
+    T err = abs(val) % period;
+    return (err < (period / 2)) ? err : (period - err);
+}
+
+template<class T>
+static int compare(const T *lhs, const T *rhs) {
+    if (*lhs < *rhs) {
+        return -1;
+    } else if (*lhs > *rhs) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+/* ======================================================================= */
+/*                                   PLL                                   */
+/* ======================================================================= */
+
+static const size_t kMinSamplesToStartPrime = 3;
+static const size_t kMinSamplesToStopPrime = VideoFrameScheduler::kHistorySize;
+static const size_t kMinSamplesToEstimatePeriod = 3;
+static const size_t kMaxSamplesToEstimatePeriod = VideoFrameScheduler::kHistorySize;
+
+static const size_t kPrecision = 12;
+static const size_t kErrorThreshold = (1 << (kPrecision * 2)) / 10;
+static const int64_t kMultiplesThresholdDiv = 4;            // 25%
+static const int64_t kReFitThresholdDiv = 100;              // 1%
+static const nsecs_t kMaxAllowedFrameSkip = kNanosIn1s;     // 1 sec
+static const nsecs_t kMinPeriod = kNanosIn1s / 120;         // 120Hz
+static const nsecs_t kRefitRefreshPeriod = 10 * kNanosIn1s; // 10 sec
+
+VideoFrameScheduler::PLL::PLL()
+    : mPeriod(-1),
+      mPhase(0),
+      mPrimed(false),
+      mSamplesUsedForPriming(0),
+      mLastTime(-1),
+      mNumSamples(0) {
+}
+
+void VideoFrameScheduler::PLL::reset(float fps) {
+    //test();
+
+    mSamplesUsedForPriming = 0;
+    mLastTime = -1;
+
+    // set up or reset video PLL
+    if (fps <= 0.f) {
+        mPeriod = -1;
+        mPrimed = false;
+    } else {
+        ALOGV("reset at %.1f fps", fps);
+        mPeriod = (nsecs_t)(1e9 / fps + 0.5);
+        mPrimed = true;
+    }
+
+    restart();
+}
+
+// reset PLL but keep previous period estimate
+void VideoFrameScheduler::PLL::restart() {
+    mNumSamples = 0;
+    mPhase = -1;
+}
+
+#if 0
+
+void VideoFrameScheduler::PLL::test() {
+    nsecs_t period = kNanosIn1s / 60;
+    mTimes[0] = 0;
+    mTimes[1] = period;
+    mTimes[2] = period * 3;
+    mTimes[3] = period * 4;
+    mTimes[4] = period * 7;
+    mTimes[5] = period * 8;
+    mTimes[6] = period * 10;
+    mTimes[7] = period * 12;
+    mNumSamples = 8;
+    int64_t a, b, err;
+    fit(0, period * 12 / 7, 8, &a, &b, &err);
+    // a = 0.8(5)+
+    // b = -0.14097(2)+
+    // err = 0.2750578(703)+
+    ALOGD("a=%lld (%.6f), b=%lld (%.6f), err=%lld (%.6f)",
+            (long long)a, (a / (float)(1 << kPrecision)),
+            (long long)b, (b / (float)(1 << kPrecision)),
+            (long long)err, (err / (float)(1 << (kPrecision * 2))));
+}
+
+#endif
+
+void VideoFrameScheduler::PLL::fit(
+        nsecs_t phase, nsecs_t period, size_t numSamplesToUse,
+        int64_t *a, int64_t *b, int64_t *err) {
+    if (numSamplesToUse > mNumSamples) {
+        numSamplesToUse = mNumSamples;
+    }
+
+    int64_t sumX = 0;
+    int64_t sumXX = 0;
+    int64_t sumXY = 0;
+    int64_t sumYY = 0;
+    int64_t sumY = 0;
+
+    int64_t x = 0; // x usually is in [0..numSamplesToUse)
+    nsecs_t lastTime;
+    for (size_t i = 0; i < numSamplesToUse; i++) {
+        size_t ix = (mNumSamples - numSamplesToUse + i) % kHistorySize;
+        nsecs_t time = mTimes[ix];
+        if (i > 0) {
+            x += divRound(time - lastTime, period);
+        }
+        // y is usually in [-numSamplesToUse..numSamplesToUse+kRefitRefreshPeriod/kMinPeriod) << kPrecision
+        //   ideally in [0..numSamplesToUse), but shifted by -numSamplesToUse during
+        //   priming, and possibly shifted by up to kRefitRefreshPeriod/kMinPeriod
+        //   while we are not refitting.
+        int64_t y = divRound(time - phase, period >> kPrecision);
+        sumX += x;
+        sumY += y;
+        sumXX += x * x;
+        sumXY += x * y;
+        sumYY += y * y;
+        lastTime = time;
+    }
+
+    int64_t div   = numSamplesToUse * sumXX - sumX * sumX;
+    int64_t a_nom = numSamplesToUse * sumXY - sumX * sumY;
+    int64_t b_nom = sumXX * sumY            - sumX * sumXY;
+    *a = divRound(a_nom, div);
+    *b = divRound(b_nom, div);
+    // don't use a and b directly as the rounding error is significant
+    *err = sumYY - divRound(a_nom * sumXY + b_nom * sumY, div);
+    ALOGV("fitting[%zu] a=%lld (%.6f), b=%lld (%.6f), err=%lld (%.6f)",
+            numSamplesToUse,
+            (long long)*a,   (*a / (float)(1 << kPrecision)),
+            (long long)*b,   (*b / (float)(1 << kPrecision)),
+            (long long)*err, (*err / (float)(1 << (kPrecision * 2))));
+}
+
+void VideoFrameScheduler::PLL::prime(size_t numSamplesToUse) {
+    if (numSamplesToUse > mNumSamples) {
+        numSamplesToUse = mNumSamples;
+    }
+    CHECK(numSamplesToUse >= 3);  // must have at least 3 samples
+
+    // estimate video framerate from deltas between timestamps, and
+    // 2nd order deltas
+    Vector<nsecs_t> deltas;
+    nsecs_t lastTime, firstTime;
+    for (size_t i = 0; i < numSamplesToUse; ++i) {
+        size_t index = (mNumSamples - numSamplesToUse + i) % kHistorySize;
+        nsecs_t time = mTimes[index];
+        if (i > 0) {
+            if (time - lastTime > kMinPeriod) {
+                //ALOGV("delta: %lld", (long long)(time - lastTime));
+                deltas.push(time - lastTime);
+            }
+        } else {
+            firstTime = time;
+        }
+        lastTime = time;
+    }
+    deltas.sort(compare<nsecs_t>);
+    size_t numDeltas = deltas.size();
+    if (numDeltas > 1) {
+        nsecs_t deltaMinLimit = min(deltas[0] / kMultiplesThresholdDiv, kMinPeriod);
+        nsecs_t deltaMaxLimit = deltas[numDeltas / 2] * kMultiplesThresholdDiv;
+        for (size_t i = numDeltas / 2 + 1; i < numDeltas; ++i) {
+            if (deltas[i] > deltaMaxLimit) {
+                deltas.resize(i);
+                numDeltas = i;
+                break;
+            }
+        }
+        for (size_t i = 1; i < numDeltas; ++i) {
+            nsecs_t delta2nd = deltas[i] - deltas[i - 1];
+            if (delta2nd >= deltaMinLimit) {
+                //ALOGV("delta2: %lld", (long long)(delta2nd));
+                deltas.push(delta2nd);
+            }
+        }
+    }
+
+    // use the one that yields the best match
+    int64_t bestScore;
+    for (size_t i = 0; i < deltas.size(); ++i) {
+        nsecs_t delta = deltas[i];
+        int64_t score = 0;
+#if 1
+        // simplest score: number of deltas that are near multiples
+        size_t matches = 0;
+        for (size_t j = 0; j < deltas.size(); ++j) {
+            nsecs_t err = periodicError(deltas[j], delta);
+            if (err < delta / kMultiplesThresholdDiv) {
+                ++matches;
+            }
+        }
+        score = matches;
+#if 0
+        // could be weighed by the (1 - normalized error)
+        if (numSamplesToUse >= kMinSamplesToEstimatePeriod) {
+            int64_t a, b, err;
+            fit(firstTime, delta, numSamplesToUse, &a, &b, &err);
+            err = (1 << (2 * kPrecision)) - err;
+            score *= max(0, err);
+        }
+#endif
+#else
+        // or use the error as a negative score
+        if (numSamplesToUse >= kMinSamplesToEstimatePeriod) {
+            int64_t a, b, err;
+            fit(firstTime, delta, numSamplesToUse, &a, &b, &err);
+            score = -delta * err;
+        }
+#endif
+        if (i == 0 || score > bestScore) {
+            bestScore = score;
+            mPeriod = delta;
+            mPhase = firstTime;
+        }
+    }
+    ALOGV("priming[%zu] phase:%lld period:%lld", numSamplesToUse, mPhase, mPeriod);
+}
+
+nsecs_t VideoFrameScheduler::PLL::addSample(nsecs_t time) {
+    if (mLastTime >= 0
+            // if time goes backward, or we skipped rendering
+            && (time > mLastTime + kMaxAllowedFrameSkip || time < mLastTime)) {
+        restart();
+    }
+
+    mLastTime = time;
+    mTimes[mNumSamples % kHistorySize] = time;
+    ++mNumSamples;
+
+    bool doFit = time > mRefitAt;
+    if ((mPeriod <= 0 || !mPrimed) && mNumSamples >= kMinSamplesToStartPrime) {
+        prime(kMinSamplesToStopPrime);
+        ++mSamplesUsedForPriming;
+        doFit = true;
+    }
+    if (mPeriod > 0 && mNumSamples >= kMinSamplesToEstimatePeriod) {
+        if (mPhase < 0) {
+            // initialize phase to the current render time
+            mPhase = time;
+            doFit = true;
+        } else if (!doFit) {
+            int64_t err = periodicError(time - mPhase, mPeriod);
+            doFit = err > mPeriod / kReFitThresholdDiv;
+        }
+
+        if (doFit) {
+            int64_t a, b, err;
+            mRefitAt = time + kRefitRefreshPeriod;
+            fit(mPhase, mPeriod, kMaxSamplesToEstimatePeriod, &a, &b, &err);
+            mPhase += (mPeriod * b) >> kPrecision;
+            mPeriod = (mPeriod * a) >> kPrecision;
+            ALOGV("new phase:%lld period:%lld", (long long)mPhase, (long long)mPeriod);
+
+            if (err < kErrorThreshold) {
+                if (!mPrimed && mSamplesUsedForPriming >= kMinSamplesToStopPrime) {
+                    mPrimed = true;
+                }
+            } else {
+                mPrimed = false;
+                mSamplesUsedForPriming = 0;
+            }
+        }
+    }
+    return mPeriod;
+}
+
+/* ======================================================================= */
+/*                             Frame Scheduler                             */
+/* ======================================================================= */
+
+static const nsecs_t kDefaultVsyncPeriod = kNanosIn1s / 60;  // 60Hz
+static const nsecs_t kVsyncRefreshPeriod = kNanosIn1s;       // 1 sec
+
+VideoFrameScheduler::VideoFrameScheduler()
+    : mVsyncTime(0),
+      mVsyncPeriod(0),
+      mVsyncRefreshAt(0),
+      mLastVsyncTime(-1),
+      mTimeCorrection(0) {
+}
+
+void VideoFrameScheduler::updateVsync() {
+    mVsyncRefreshAt = systemTime(SYSTEM_TIME_MONOTONIC) + kVsyncRefreshPeriod;
+    mVsyncPeriod = 0;
+    mVsyncTime = 0;
+
+    // TODO: schedule frames for the destination surface
+    // For now, surface flinger only schedules frames on the primary display
+    if (mComposer == NULL) {
+        String16 name("SurfaceFlinger");
+        sp<IServiceManager> sm = defaultServiceManager();
+        mComposer = interface_cast<ISurfaceComposer>(sm->checkService(name));
+    }
+    if (mComposer != NULL) {
+        DisplayStatInfo stats;
+        status_t res = mComposer->getDisplayStats(NULL /* display */, &stats);
+        if (res == OK) {
+            ALOGV("vsync time:%lld period:%lld",
+                    (long long)stats.vsyncTime, (long long)stats.vsyncPeriod);
+            mVsyncTime = stats.vsyncTime;
+            mVsyncPeriod = stats.vsyncPeriod;
+        } else {
+            ALOGW("getDisplayStats returned %d", res);
+        }
+    } else {
+        ALOGW("could not get surface mComposer service");
+    }
+}
+
+void VideoFrameScheduler::init(float videoFps) {
+    updateVsync();
+
+    mLastVsyncTime = -1;
+    mTimeCorrection = 0;
+
+    mPll.reset(videoFps);
+}
+
+void VideoFrameScheduler::restart() {
+    mLastVsyncTime = -1;
+    mTimeCorrection = 0;
+
+    mPll.restart();
+}
+
+nsecs_t VideoFrameScheduler::getVsyncPeriod() {
+    if (mVsyncPeriod > 0) {
+        return mVsyncPeriod;
+    }
+    return kDefaultVsyncPeriod;
+}
+
+nsecs_t VideoFrameScheduler::schedule(nsecs_t renderTime) {
+    nsecs_t origRenderTime = renderTime;
+
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    if (now >= mVsyncRefreshAt) {
+        updateVsync();
+    }
+
+    // without VSYNC info, there is nothing to do
+    if (mVsyncPeriod == 0) {
+        ALOGV("no vsync: render=%lld", (long long)renderTime);
+        return renderTime;
+    }
+
+    // ensure vsync time is well before (corrected) render time
+    if (mVsyncTime > renderTime - 4 * mVsyncPeriod) {
+        mVsyncTime -=
+            ((mVsyncTime - renderTime) / mVsyncPeriod + 5) * mVsyncPeriod;
+    }
+
+    // Video presentation takes place at the VSYNC _after_ renderTime.  Adjust renderTime
+    // so this effectively becomes a rounding operation (to the _closest_ VSYNC.)
+    renderTime -= mVsyncPeriod / 2;
+
+    const nsecs_t videoPeriod = mPll.addSample(origRenderTime);
+    if (videoPeriod > 0) {
+        // Smooth out rendering
+        size_t N = 12;
+        nsecs_t fiveSixthDev =
+            abs(((videoPeriod * 5 + mVsyncPeriod) % (mVsyncPeriod * 6)) - mVsyncPeriod)
+                    / (mVsyncPeriod / 100);
+        // use 20 samples if we are doing 5:6 ratio +- 1% (e.g. playing 50Hz on 60Hz)
+        if (fiveSixthDev < 12) {  /* 12% / 6 = 2% */
+            N = 20;
+        }
+
+        nsecs_t offset = 0;
+        nsecs_t edgeRemainder = 0;
+        for (size_t i = 1; i <= N; i++) {
+            offset +=
+                (renderTime + mTimeCorrection + videoPeriod * i - mVsyncTime) % mVsyncPeriod;
+            edgeRemainder += (videoPeriod * i) % mVsyncPeriod;
+        }
+        mTimeCorrection += mVsyncPeriod / 2 - offset / N;
+        renderTime += mTimeCorrection;
+        nsecs_t correctionLimit = mVsyncPeriod * 3 / 5;
+        edgeRemainder = abs(edgeRemainder / N - mVsyncPeriod / 2);
+        if (edgeRemainder <= mVsyncPeriod / 3) {
+            correctionLimit /= 2;
+        }
+
+        // estimate how many VSYNCs a frame will spend on the display
+        nsecs_t nextVsyncTime =
+            renderTime + mVsyncPeriod - ((renderTime - mVsyncTime) % mVsyncPeriod);
+        if (mLastVsyncTime >= 0) {
+            size_t minVsyncsPerFrame = videoPeriod / mVsyncPeriod;
+            size_t vsyncsForLastFrame = divRound(nextVsyncTime - mLastVsyncTime, mVsyncPeriod);
+            bool vsyncsPerFrameAreNearlyConstant =
+                periodicError(videoPeriod, mVsyncPeriod) / (mVsyncPeriod / 20) == 0;
+
+            if (mTimeCorrection > correctionLimit &&
+                    (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame > minVsyncsPerFrame)) {
+                // remove a VSYNC
+                mTimeCorrection -= mVsyncPeriod / 2;
+                renderTime -= mVsyncPeriod / 2;
+                nextVsyncTime -= mVsyncPeriod;
+                --vsyncsForLastFrame;
+            } else if (mTimeCorrection < -correctionLimit &&
+                    (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame == minVsyncsPerFrame)) {
+                // add a VSYNC
+                mTimeCorrection += mVsyncPeriod / 2;
+                renderTime += mVsyncPeriod / 2;
+                nextVsyncTime += mVsyncPeriod;
+                ++vsyncsForLastFrame;
+            }
+            ATRACE_INT("FRAME_VSYNCS", vsyncsForLastFrame);
+        }
+        mLastVsyncTime = nextVsyncTime;
+    }
+
+    // align rendertime to the center between VSYNC edges
+    renderTime -= (renderTime - mVsyncTime) % mVsyncPeriod;
+    renderTime += mVsyncPeriod / 2;
+    ALOGV("adjusting render: %lld => %lld", (long long)origRenderTime, (long long)renderTime);
+    ATRACE_INT("FRAME_FLIP_IN(ms)", (renderTime - now) / 1000000);
+    return renderTime;
+}
+
+void VideoFrameScheduler::release() {
+    mComposer.clear();
+}
+
+VideoFrameScheduler::~VideoFrameScheduler() {
+    release();
+}
+
+} // namespace android
+
diff --git a/media/libmediaplayerservice/VideoFrameScheduler.h b/media/libmediaplayerservice/VideoFrameScheduler.h
new file mode 100644
index 0000000..19f0787
--- /dev/null
+++ b/media/libmediaplayerservice/VideoFrameScheduler.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014, 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 VIDEO_FRAME_SCHEDULER_H_
+#define VIDEO_FRAME_SCHEDULER_H_
+
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct ISurfaceComposer;
+
+struct VideoFrameScheduler : public RefBase {
+    VideoFrameScheduler();
+
+    // (re)initialize scheduler
+    void init(float videoFps = -1);
+    // use in case of video render-time discontinuity, e.g. seek
+    void restart();
+    // get adjusted nanotime for a video frame render at renderTime
+    nsecs_t schedule(nsecs_t renderTime);
+
+    // returns the vsync period for the main display
+    nsecs_t getVsyncPeriod();
+
+    void release();
+
+    static const size_t kHistorySize = 8;
+
+protected:
+    virtual ~VideoFrameScheduler();
+
+private:
+    struct PLL {
+        PLL();
+
+        // reset PLL to new PLL
+        void reset(float fps = -1);
+        // keep current estimate, but restart phase
+        void restart();
+        // returns period
+        nsecs_t addSample(nsecs_t time);
+
+    private:
+        nsecs_t mPeriod;
+        nsecs_t mPhase;
+
+        bool    mPrimed;        // have an estimate for the period
+        size_t  mSamplesUsedForPriming;
+
+        nsecs_t mLastTime;      // last input time
+        nsecs_t mRefitAt;       // next input time to fit at
+
+        size_t  mNumSamples;    // can go past kHistorySize
+        nsecs_t mTimes[kHistorySize];
+
+        void test();
+        void fit(nsecs_t phase, nsecs_t period, size_t numSamples,
+                int64_t *a, int64_t *b, int64_t *err);
+        void prime(size_t numSamples);
+    };
+
+    void updateVsync();
+
+    nsecs_t mVsyncTime;        // vsync timing from display
+    nsecs_t mVsyncPeriod;
+    nsecs_t mVsyncRefreshAt;   // next time to refresh timing info
+
+    nsecs_t mLastVsyncTime;    // estimated vsync time for last frame
+    nsecs_t mTimeCorrection;   // running adjustment
+
+    PLL mPll;                  // PLL for video frame rate based on render time
+
+    sp<ISurfaceComposer> mComposer;
+
+    DISALLOW_EVIL_CONSTRUCTORS(VideoFrameScheduler);
+};
+
+}  // namespace android
+
+#endif  // VIDEO_FRAME_SCHEDULER_H_
+
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index 0dd2b61..676c0a6 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -19,6 +19,7 @@
 	$(TOP)/frameworks/av/media/libstagefright/mpeg2ts             \
 	$(TOP)/frameworks/av/media/libstagefright/rtsp                \
 	$(TOP)/frameworks/av/media/libstagefright/timedtext           \
+	$(TOP)/frameworks/av/media/libmediaplayerservice              \
 	$(TOP)/frameworks/native/include/media/openmax
 
 LOCAL_MODULE:= libstagefright_nuplayer
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 142107d..bd75034 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -158,7 +158,12 @@
 
     int32_t totalBitrate = 0;
 
-    for (size_t i = 0; i < extractor->countTracks(); ++i) {
+    size_t numtracks = extractor->countTracks();
+    if (numtracks == 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    for (size_t i = 0; i < numtracks; ++i) {
         sp<MediaSource> track = extractor->getTrack(i);
 
         sp<MetaData> meta = extractor->getTrackMetaData(i);
@@ -1110,8 +1115,8 @@
     if (mIsWidevine && !audio) {
         // data is already provided in the buffer
         ab = new ABuffer(NULL, mb->range_length());
-        ab->meta()->setPointer("mediaBuffer", mb);
         mb->add_ref();
+        ab->setMediaBufferBase(mb);
     } else {
         ab = new ABuffer(outLength);
         memcpy(ab->data(),
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 5321deb..ceedb40 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -64,16 +64,18 @@
 };
 
 struct NuPlayer::SeekAction : public Action {
-    SeekAction(int64_t seekTimeUs)
-        : mSeekTimeUs(seekTimeUs) {
+    SeekAction(int64_t seekTimeUs, bool needNotify)
+        : mSeekTimeUs(seekTimeUs),
+          mNeedNotify(needNotify) {
     }
 
     virtual void execute(NuPlayer *player) {
-        player->performSeek(mSeekTimeUs);
+        player->performSeek(mSeekTimeUs, mNeedNotify);
     }
 
 private:
     int64_t mSeekTimeUs;
+    bool mNeedNotify;
 
     DISALLOW_EVIL_CONSTRUCTORS(SeekAction);
 };
@@ -155,6 +157,7 @@
       mCurrentOffloadInfo(AUDIO_INFO_INITIALIZER),
       mAudioDecoderGeneration(0),
       mVideoDecoderGeneration(0),
+      mRendererGeneration(0),
       mAudioEOS(false),
       mVideoEOS(false),
       mScanSourcesPending(false),
@@ -323,9 +326,10 @@
     (new AMessage(kWhatReset, id()))->post();
 }
 
-void NuPlayer::seekToAsync(int64_t seekTimeUs) {
+void NuPlayer::seekToAsync(int64_t seekTimeUs, bool needNotify) {
     sp<AMessage> msg = new AMessage(kWhatSeek, id());
     msg->setInt64("seekTimeUs", seekTimeUs);
+    msg->setInt32("needNotify", needNotify);
     msg->post();
 }
 
@@ -560,7 +564,8 @@
                     // the extractor may not yet be started and will assert.
                     // If the video decoder is not set (perhaps audio only in this case)
                     // do not perform a seek as it is not needed.
-                    mDeferredActions.push_back(new SeekAction(mCurrentPositionUs));
+                    mDeferredActions.push_back(
+                            new SeekAction(mCurrentPositionUs, false /* needNotify */));
                 }
 
                 // If there is a new surface texture, instantiate decoders
@@ -633,16 +638,23 @@
                 flags |= Renderer::FLAG_OFFLOAD_AUDIO;
             }
 
-            mRenderer = new Renderer(
-                    mAudioSink,
-                    new AMessage(kWhatRendererNotify, id()),
-                    flags);
+            sp<AMessage> notify = new AMessage(kWhatRendererNotify, id());
+            ++mRendererGeneration;
+            notify->setInt32("generation", mRendererGeneration);
+            mRenderer = new Renderer(mAudioSink, notify, flags);
 
             mRendererLooper = new ALooper;
             mRendererLooper->setName("NuPlayerRenderer");
             mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
             mRendererLooper->registerHandler(mRenderer);
 
+            sp<MetaData> meta = getFileMeta();
+            int32_t rate;
+            if (meta != NULL
+                    && meta->findInt32(kKeyFrameRate, &rate) && rate > 0) {
+                mRenderer->setVideoFrameRate(rate);
+            }
+
             postScanSources();
             break;
         }
@@ -806,11 +818,13 @@
                 ALOGV("%s shutdown completed", audio ? "audio" : "video");
                 if (audio) {
                     mAudioDecoder.clear();
+                    ++mAudioDecoderGeneration;
 
                     CHECK_EQ((int)mFlushingAudio, (int)SHUTTING_DOWN_DECODER);
                     mFlushingAudio = SHUT_DOWN;
                 } else {
                     mVideoDecoder.clear();
+                    ++mVideoDecoderGeneration;
 
                     CHECK_EQ((int)mFlushingVideo, (int)SHUTTING_DOWN_DECODER);
                     mFlushingVideo = SHUT_DOWN;
@@ -828,9 +842,11 @@
                 mRenderer->queueEOS(audio, err);
                 if (audio && mFlushingAudio != NONE) {
                     mAudioDecoder.clear();
+                    ++mAudioDecoderGeneration;
                     mFlushingAudio = SHUT_DOWN;
                 } else if (!audio && mFlushingVideo != NONE){
                     mVideoDecoder.clear();
+                    ++mVideoDecoderGeneration;
                     mFlushingVideo = SHUT_DOWN;
                 }
                 finishFlushIfPossible();
@@ -850,6 +866,14 @@
 
         case kWhatRendererNotify:
         {
+            int32_t requesterGeneration = mRendererGeneration - 1;
+            CHECK(msg->findInt32("generation", &requesterGeneration));
+            if (requesterGeneration != mRendererGeneration) {
+                ALOGV("got message from old renderer, generation(%d:%d)",
+                        requesterGeneration, mRendererGeneration);
+                return;
+            }
+
             int32_t what;
             CHECK(msg->findInt32("what", &what));
 
@@ -912,6 +936,7 @@
                 CHECK(msg->findInt64("positionUs", &positionUs));
                 closeAudioSink();
                 mAudioDecoder.clear();
+                ++mAudioDecoderGeneration;
                 mRenderer->flush(true /* audio */);
                 if (mVideoDecoder != NULL) {
                     mRenderer->flush(false /* audio */);
@@ -919,7 +944,7 @@
                 mRenderer->signalDisableOffloadAudio();
                 mOffloadAudio = false;
 
-                performSeek(positionUs);
+                performSeek(positionUs, false /* needNotify */);
                 instantiateDecoder(true /* audio */, &mAudioDecoder);
             }
             break;
@@ -948,14 +973,18 @@
         case kWhatSeek:
         {
             int64_t seekTimeUs;
+            int32_t needNotify;
             CHECK(msg->findInt64("seekTimeUs", &seekTimeUs));
+            CHECK(msg->findInt32("needNotify", &needNotify));
 
-            ALOGV("kWhatSeek seekTimeUs=%lld us", seekTimeUs);
+            ALOGV("kWhatSeek seekTimeUs=%lld us, needNotify=%d",
+                    seekTimeUs, needNotify);
 
             mDeferredActions.push_back(
                     new SimpleAction(&NuPlayer::performDecoderFlush));
 
-            mDeferredActions.push_back(new SeekAction(seekTimeUs));
+            mDeferredActions.push_back(
+                    new SeekAction(seekTimeUs, needNotify));
 
             processDeferredActions();
             break;
@@ -963,17 +992,31 @@
 
         case kWhatPause:
         {
-            CHECK(mRenderer != NULL);
-            mSource->pause();
-            mRenderer->pause();
+            if (mSource != NULL) {
+                mSource->pause();
+            } else {
+                ALOGW("pause called when source is gone or not set");
+            }
+            if (mRenderer != NULL) {
+                mRenderer->pause();
+            } else {
+                ALOGW("pause called when renderer is gone or not set");
+            }
             break;
         }
 
         case kWhatResume:
         {
-            CHECK(mRenderer != NULL);
-            mSource->resume();
-            mRenderer->resume();
+            if (mSource != NULL) {
+                mSource->resume();
+            } else {
+                ALOGW("resume called when source is gone or not set");
+            }
+            if (mRenderer != NULL) {
+                mRenderer->resume();
+            } else {
+                ALOGW("resume called when renderer is gone or not set");
+            }
             break;
         }
 
@@ -1270,8 +1313,8 @@
 
     // Aggregate smaller buffers into a larger buffer.
     // The goal is to reduce power consumption.
-    // Unfortunately this does not work with the software AAC decoder.
-    bool doBufferAggregation = (audio && mOffloadAudio);;
+    // Note this will not work if the decoder requires one frame per buffer.
+    bool doBufferAggregation = (audio && mOffloadAudio);
     bool needMoreData = false;
 
     bool dropAccessUnit;
@@ -1291,7 +1334,7 @@
             return err;
         } else if (err != OK) {
             if (err == INFO_DISCONTINUITY) {
-                if (mAggregateBuffer != NULL) {
+                if (doBufferAggregation && (mAggregateBuffer != NULL)) {
                     // We already have some data so save this for later.
                     mPendingAudioErr = err;
                     mPendingAudioAccessUnit = accessUnit;
@@ -1414,7 +1457,7 @@
             mAggregateBuffer->setRange(0, 0); // start empty
         }
 
-        if (mAggregateBuffer != NULL) {
+        if (doBufferAggregation && (mAggregateBuffer != NULL)) {
             int64_t timeUs;
             int64_t dummy;
             bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs);
@@ -1463,7 +1506,7 @@
         mCCDecoder->decode(accessUnit);
     }
 
-    if (mAggregateBuffer != NULL) {
+    if (doBufferAggregation && (mAggregateBuffer != NULL)) {
         ALOGV("feedDecoderInputData() reply with aggregated buffer, %zu",
                 mAggregateBuffer->size());
         reply->setBuffer("buffer", mAggregateBuffer);
@@ -1767,10 +1810,11 @@
     }
 }
 
-void NuPlayer::performSeek(int64_t seekTimeUs) {
-    ALOGV("performSeek seekTimeUs=%lld us (%.2f secs)",
+void NuPlayer::performSeek(int64_t seekTimeUs, bool needNotify) {
+    ALOGV("performSeek seekTimeUs=%lld us (%.2f secs), needNotify(%d)",
           seekTimeUs,
-          seekTimeUs / 1E6);
+          seekTimeUs / 1E6,
+          needNotify);
 
     if (mSource == NULL) {
         // This happens when reset occurs right before the loop mode
@@ -1787,7 +1831,9 @@
         sp<NuPlayerDriver> driver = mDriver.promote();
         if (driver != NULL) {
             driver->notifyPosition(seekTimeUs);
-            driver->notifySeekComplete();
+            if (needNotify) {
+                driver->notifySeekComplete();
+            }
         }
     }
 
@@ -1842,9 +1888,6 @@
     ++mScanSourcesGeneration;
     mScanSourcesPending = false;
 
-    ++mAudioDecoderGeneration;
-    ++mVideoDecoderGeneration;
-
     if (mRendererLooper != NULL) {
         if (mRenderer != NULL) {
             mRendererLooper->unregisterHandler(mRenderer->id());
@@ -1853,6 +1896,7 @@
         mRendererLooper.clear();
     }
     mRenderer.clear();
+    ++mRendererGeneration;
 
     if (mSource != NULL) {
         mSource->stop();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 7197e5f..8fa7269 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -59,8 +59,9 @@
     // Will notify the driver through "notifyResetComplete" once finished.
     void resetAsync();
 
-    // Will notify the driver through "notifySeekComplete" once finished.
-    void seekToAsync(int64_t seekTimeUs);
+    // Will notify the driver through "notifySeekComplete" once finished
+    // and needNotify is true.
+    void seekToAsync(int64_t seekTimeUs, bool needNotify = false);
 
     status_t setVideoScalingMode(int32_t mode);
     status_t getTrackInfo(Parcel* reply) const;
@@ -137,6 +138,7 @@
     sp<ALooper> mRendererLooper;
     int32_t mAudioDecoderGeneration;
     int32_t mVideoDecoderGeneration;
+    int32_t mRendererGeneration;
 
     List<sp<Action> > mDeferredActions;
 
@@ -214,7 +216,7 @@
 
     void processDeferredActions();
 
-    void performSeek(int64_t seekTimeUs);
+    void performSeek(int64_t seekTimeUs, bool needNotify);
     void performDecoderFlush();
     void performDecoderShutdown(bool audio, bool video);
     void performReset();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 87f85e7..cdb860c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -283,14 +283,9 @@
 
     // handle widevine classic source - that fills an arbitrary input buffer
     MediaBuffer *mediaBuffer = NULL;
-    if (hasBuffer && buffer->meta()->findPointer(
-            "mediaBuffer", (void **)&mediaBuffer)) {
-        if (mediaBuffer == NULL) {
-            // received no actual buffer
-            ALOGW("[%s] received null MediaBuffer %s",
-                    mComponentName.c_str(), msg->debugString().c_str());
-            buffer = NULL;
-        } else {
+    if (hasBuffer) {
+        mediaBuffer = (MediaBuffer *)(buffer->getMediaBufferBase());
+        if (mediaBuffer != NULL) {
             // likely filled another buffer than we requested: adjust buffer index
             size_t ix;
             for (ix = 0; ix < mInputBuffers.size(); ix++) {
@@ -470,7 +465,9 @@
     size_t bufferIx;
     CHECK(msg->findSize("buffer-ix", &bufferIx));
     if (msg->findInt32("render", &render) && render) {
-        err = mCodec->renderOutputBufferAndRelease(bufferIx);
+        int64_t timestampNs;
+        CHECK(msg->findInt64("timestampNs", &timestampNs));
+        err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs);
     } else {
         err = mCodec->releaseOutputBuffer(bufferIx);
     }
@@ -598,16 +595,6 @@
         {
             if (!isStaleReply(msg)) {
                 onInputBufferFilled(msg);
-            } else {
-                /* release any MediaBuffer passed in the stale buffer */
-                sp<ABuffer> buffer;
-                MediaBuffer *mediaBuffer = NULL;
-                if (msg->findBuffer("buffer", &buffer) &&
-                    buffer->meta()->findPointer(
-                            "mediaBuffer", (void **)&mediaBuffer) &&
-                    mediaBuffer != NULL) {
-                    mediaBuffer->release();
-                }
             }
 
             break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 7ec9876..a9bca49 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -240,9 +240,7 @@
             mPlayer->start();
 
             if (mStartupSeekTimeUs >= 0) {
-                if (mStartupSeekTimeUs == 0) {
-                    notifySeekComplete_l();
-                } else {
+                if (mStartupSeekTimeUs > 0) {
                     mPlayer->seekToAsync(mStartupSeekTimeUs);
                 }
 
@@ -369,7 +367,7 @@
             mAtEOS = false;
             // seeks can take a while, so we essentially paused
             notifyListener_l(MEDIA_PAUSED);
-            mPlayer->seekToAsync(seekTimeUs);
+            mPlayer->seekToAsync(seekTimeUs, true /* needNotify */);
             break;
         }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 7674616..73ac057 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -26,10 +26,16 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 
+#include <VideoFrameScheduler.h>
+
 #include <inttypes.h>
 
 namespace android {
 
+// Maximum time in paused state when offloading audio decompression. When elapsed, the AudioSink
+// is closed to allow the audio DSP to power down.
+static const int64_t kOffloadPauseMaxUs = 60000000ll;
+
 // static
 const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll;
 
@@ -59,7 +65,9 @@
       mVideoRenderingStartGeneration(0),
       mAudioRenderingStartGeneration(0),
       mLastPositionUpdateUs(-1ll),
-      mVideoLateByUs(0ll) {
+      mVideoLateByUs(0ll),
+      mAudioOffloadPauseTimeoutGeneration(0),
+      mAudioOffloadTornDown(false) {
 }
 
 NuPlayer::Renderer::~Renderer() {
@@ -137,6 +145,12 @@
     (new AMessage(kWhatResume, id()))->post();
 }
 
+void NuPlayer::Renderer::setVideoFrameRate(float fps) {
+    sp<AMessage> msg = new AMessage(kWhatSetVideoFrameRate, id());
+    msg->setFloat("frame-rate", fps);
+    msg->post();
+}
+
 void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
         case kWhatStopAudioSink:
@@ -237,12 +251,31 @@
             break;
         }
 
+        case kWhatSetVideoFrameRate:
+        {
+            float fps;
+            CHECK(msg->findFloat("frame-rate", &fps));
+            onSetVideoFrameRate(fps);
+            break;
+        }
+
         case kWhatAudioOffloadTearDown:
         {
             onAudioOffloadTearDown();
             break;
         }
 
+        case kWhatAudioOffloadPauseTimeout:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            if (generation != mAudioOffloadPauseTimeoutGeneration) {
+                break;
+            }
+            onAudioOffloadTearDown();
+            break;
+        }
+
         default:
             TRESPASS();
             break;
@@ -502,16 +535,20 @@
     sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id());
     msg->setInt32("generation", mVideoQueueGeneration);
 
-    int64_t delayUs;
-
     if (entry.mBuffer == NULL) {
         // EOS doesn't carry a timestamp.
-        delayUs = 0;
-    } else if (mFlags & FLAG_REAL_TIME) {
+        msg->post();
+        mDrainVideoQueuePending = true;
+        return;
+    }
+
+    int64_t delayUs;
+    int64_t nowUs = ALooper::GetNowUs();
+    int64_t realTimeUs;
+    if (mFlags & FLAG_REAL_TIME) {
         int64_t mediaTimeUs;
         CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
-
-        delayUs = mediaTimeUs - ALooper::GetNowUs();
+        realTimeUs = mediaTimeUs;
     } else {
         int64_t mediaTimeUs;
         CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
@@ -520,23 +557,26 @@
             mFirstAnchorTimeMediaUs = mediaTimeUs;
         }
         if (mAnchorTimeMediaUs < 0) {
-            delayUs = 0;
-
             if (!mHasAudio) {
                 mAnchorTimeMediaUs = mediaTimeUs;
-                mAnchorTimeRealUs = ALooper::GetNowUs();
+                mAnchorTimeRealUs = nowUs;
                 notifyPosition();
             }
+            realTimeUs = nowUs;
         } else {
-            int64_t realTimeUs =
+            realTimeUs =
                 (mediaTimeUs - mAnchorTimeMediaUs) + mAnchorTimeRealUs;
-
-            delayUs = realTimeUs - ALooper::GetNowUs();
         }
     }
 
+    realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000;
+    int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000);
+
+    delayUs = realTimeUs - nowUs;
+
     ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs);
-    msg->post(delayUs);
+    // post 2 display refreshes before rendering is due
+    msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0);
 
     mDrainVideoQueuePending = true;
 }
@@ -588,6 +628,7 @@
         mVideoLateByUs = 0ll;
     }
 
+    entry->mNotifyConsumed->setInt64("timestampNs", realTimeUs * 1000ll);
     entry->mNotifyConsumed->setInt32("render", !tooLate);
     entry->mNotifyConsumed->post();
     mVideoQueue.erase(mVideoQueue.begin());
@@ -630,6 +671,10 @@
         mHasAudio = true;
     } else {
         mHasVideo = true;
+        if (mVideoScheduler == NULL) {
+            mVideoScheduler = new VideoFrameScheduler();
+            mVideoScheduler->init();
+        }
     }
 
     if (dropBufferWhileFlushing(audio, msg)) {
@@ -795,6 +840,10 @@
         mDrainVideoQueuePending = false;
         ++mVideoQueueGeneration;
 
+        if (mVideoScheduler != NULL) {
+            mVideoScheduler->restart();
+        }
+
         prepareForMediaRenderingStart();
     }
 
@@ -919,6 +968,7 @@
 
     if (mHasAudio) {
         mAudioSink->pause();
+        startAudioOffloadPauseTimeout();
     }
 
     ALOGV("now paused audio queue has %d entries, video has %d entries",
@@ -931,6 +981,7 @@
     }
 
     if (mHasAudio) {
+        cancelAudioOffloadPauseTimeout();
         mAudioSink->start();
     }
 
@@ -946,6 +997,13 @@
     }
 }
 
+void NuPlayer::Renderer::onSetVideoFrameRate(float fps) {
+    if (mVideoScheduler == NULL) {
+        mVideoScheduler = new VideoFrameScheduler();
+    }
+    mVideoScheduler->init(fps);
+}
+
 // TODO: Remove unnecessary calls to getPlayedOutAudioDurationUs()
 // as it acquires locks and may query the audio driver.
 //
@@ -1012,6 +1070,11 @@
 }
 
 void NuPlayer::Renderer::onAudioOffloadTearDown() {
+    if (mAudioOffloadTornDown) {
+        return;
+    }
+    mAudioOffloadTornDown = true;
+
     int64_t firstAudioTimeUs;
     {
         Mutex::Autolock autoLock(mLock);
@@ -1030,5 +1093,19 @@
     notify->post();
 }
 
+void NuPlayer::Renderer::startAudioOffloadPauseTimeout() {
+    if (offloadingAudio()) {
+        sp<AMessage> msg = new AMessage(kWhatAudioOffloadPauseTimeout, id());
+        msg->setInt32("generation", mAudioOffloadPauseTimeoutGeneration);
+        msg->post(kOffloadPauseMaxUs);
+    }
+}
+
+void NuPlayer::Renderer::cancelAudioOffloadPauseTimeout() {
+    if (offloadingAudio()) {
+        ++mAudioOffloadPauseTimeoutGeneration;
+    }
+}
+
 }  // namespace android
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 97fdae7..8e6112b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -23,6 +23,7 @@
 namespace android {
 
 struct ABuffer;
+struct VideoFrameScheduler;
 
 struct NuPlayer::Renderer : public AHandler {
     enum Flags {
@@ -56,6 +57,8 @@
     void pause();
     void resume();
 
+    void setVideoFrameRate(float fps);
+
     enum {
         kWhatEOS                 = 'eos ',
         kWhatFlushComplete       = 'fluC',
@@ -63,6 +66,7 @@
         kWhatVideoRenderingStart = 'vdrd',
         kWhatMediaRenderingStart = 'mdrd',
         kWhatAudioOffloadTearDown = 'aOTD',
+        kWhatAudioOffloadPauseTimeout = 'aOPT',
     };
 
 protected:
@@ -82,6 +86,7 @@
         kWhatResume              = 'resm',
         kWhatStopAudioSink       = 'stpA',
         kWhatDisableOffloadAudio = 'noOA',
+        kWhatSetVideoFrameRate   = 'sVFR',
     };
 
     struct QueueEntry {
@@ -100,6 +105,7 @@
     List<QueueEntry> mAudioQueue;
     List<QueueEntry> mVideoQueue;
     uint32_t mNumFramesWritten;
+    sp<VideoFrameScheduler> mVideoScheduler;
 
     bool mDrainAudioQueuePending;
     bool mDrainVideoQueuePending;
@@ -127,6 +133,9 @@
     int64_t mLastPositionUpdateUs;
     int64_t mVideoLateByUs;
 
+    int32_t mAudioOffloadPauseTimeoutGeneration;
+    bool mAudioOffloadTornDown;
+
     size_t fillAudioBuffer(void *buffer, size_t size);
 
     bool onDrainAudioQueue();
@@ -147,6 +156,7 @@
     void onDisableOffloadAudio();
     void onPause();
     void onResume();
+    void onSetVideoFrameRate(float fps);
     void onAudioOffloadTearDown();
 
     void notifyEOS(bool audio, status_t finalResult, int64_t delayUs = 0);
@@ -162,6 +172,9 @@
 
     bool offloadingAudio() const { return (mFlags & FLAG_OFFLOAD_AUDIO) != 0; }
 
+    void startAudioOffloadPauseTimeout();
+    void cancelAudioOffloadPauseTimeout();
+
     DISALLOW_EVIL_CONSTRUCTORS(Renderer);
 };
 
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 9b03b71..3c04859 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -3782,23 +3782,12 @@
     CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_COMPONENT);
     info->mStatus = BufferInfo::OWNED_BY_US;
 
-    const sp<AMessage> &bufferMeta = info->mData->meta();
-    void *mediaBuffer;
-    if (bufferMeta->findPointer("mediaBuffer", &mediaBuffer)
-            && mediaBuffer != NULL) {
-        // We're in "store-metadata-in-buffers" mode, the underlying
-        // OMX component had access to data that's implicitly refcounted
-        // by this "mediaBuffer" object. Now that the OMX component has
-        // told us that it's done with the input buffer, we can decrement
-        // the mediaBuffer's reference count.
-
-        ALOGV("releasing mbuf %p", mediaBuffer);
-
-        ((MediaBuffer *)mediaBuffer)->release();
-        mediaBuffer = NULL;
-
-        bufferMeta->setPointer("mediaBuffer", NULL);
-    }
+    // We're in "store-metadata-in-buffers" mode, the underlying
+    // OMX component had access to data that's implicitly refcounted
+    // by this "MediaBuffer" object. Now that the OMX component has
+    // told us that it's done with the input buffer, we can decrement
+    // the mediaBuffer's reference count.
+    info->mData->setMediaBufferBase(NULL);
 
     PortMode mode = getPortMode(kPortIndexInput);
 
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 9d6fd78..a72cbd5 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -199,7 +199,18 @@
     } else if (!strncasecmp("http://", uri, 7)
             || !strncasecmp("https://", uri, 8)
             || isWidevine) {
-        sp<HTTPBase> httpSource = new MediaHTTP(httpService->makeHTTPConnection());
+        if (httpService == NULL) {
+            ALOGE("Invalid http service!");
+            return NULL;
+        }
+
+        sp<IMediaHTTPConnection> conn = httpService->makeHTTPConnection();
+        if (conn == NULL) {
+            ALOGE("Failed to make http connection from http service!");
+            return NULL;
+        }
+
+        sp<HTTPBase> httpSource = new MediaHTTP(conn);
 
         String8 tmp;
         if (isWidevine) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 0bfc6e4..6c98c52 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -179,7 +179,7 @@
     response->postReply(replyID);
 }
 
-status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) {
+status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
     // save init parameters for reset
     mInitName = name;
     mInitNameIsType = nameIsType;
@@ -191,7 +191,7 @@
     // queue.
     mCodec = new ACodec;
     bool needDedicatedLooper = false;
-    if (nameIsType && !strncasecmp(name, "video/", 6)) {
+    if (nameIsType && !strncasecmp(name.c_str(), "video/", 6)) {
         needDedicatedLooper = true;
     } else {
         AString tmp = name;
@@ -357,7 +357,7 @@
     mHaveInputSurface = false;
 
     if (err == OK) {
-        err = init(mInitName.c_str(), mInitNameIsType, mInitIsEncoder);
+        err = init(mInitName, mInitNameIsType, mInitIsEncoder);
     }
     return err;
 }
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index 1a80dcc..27cd231 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -37,19 +37,6 @@
 
 namespace android {
 
-static void ReleaseMediaBufferReference(const sp<ABuffer> &accessUnit) {
-    void *mbuf;
-    if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
-            && mbuf != NULL) {
-        ALOGV("releasing mbuf %p", mbuf);
-
-        accessUnit->meta()->setPointer("mediaBuffer", NULL);
-
-        static_cast<MediaBuffer *>(mbuf)->release();
-        mbuf = NULL;
-    }
-}
-
 struct MediaCodecSource::Puller : public AHandler {
     Puller(const sp<MediaSource> &source);
 
@@ -477,7 +464,7 @@
 
     for (size_t i = 0; i < mEncoderInputBuffers.size(); ++i) {
         sp<ABuffer> accessUnit = mEncoderInputBuffers.itemAt(i);
-        ReleaseMediaBufferReference(accessUnit);
+        accessUnit->setMediaBufferBase(NULL);
     }
 
     mEncoderInputBuffers.clear();
@@ -608,8 +595,8 @@
             if (mIsVideo) {
                 // video encoder will release MediaBuffer when done
                 // with underlying data.
-                mEncoderInputBuffers.itemAt(bufferIndex)->meta()
-                        ->setPointer("mediaBuffer", mbuf);
+                mEncoderInputBuffers.itemAt(bufferIndex)->setMediaBufferBase(
+                        mbuf);
             } else {
                 mbuf->release();
             }
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 456be89..fb27dca 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -137,6 +137,7 @@
     mOutputDelayRingBuffer = new short[mOutputDelayRingBufferSize];
     mOutputDelayRingBufferWritePos = 0;
     mOutputDelayRingBufferReadPos = 0;
+    mOutputDelayRingBufferFilled = 0;
 
     if (mAACDecoder == NULL) {
         ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code");
@@ -408,6 +409,13 @@
 }
 
 bool SoftAAC2::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) {
+    if (numSamples == 0) {
+        return true;
+    }
+    if (outputDelayRingBufferSpaceLeft() < numSamples) {
+        ALOGE("RING BUFFER WOULD OVERFLOW");
+        return false;
+    }
     if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize
             && (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos
                     || mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) {
@@ -419,10 +427,6 @@
         if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
             mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
         }
-        if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
-            ALOGE("RING BUFFER OVERFLOW");
-            return false;
-        }
     } else {
         ALOGV("slow SoftAAC2::outputDelayRingBufferPutSamples()");
 
@@ -432,16 +436,19 @@
             if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
                 mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
             }
-            if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
-                ALOGE("RING BUFFER OVERFLOW");
-                return false;
-            }
         }
     }
+    mOutputDelayRingBufferFilled += numSamples;
     return true;
 }
 
 int32_t SoftAAC2::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t numSamples) {
+
+    if (numSamples > mOutputDelayRingBufferFilled) {
+        ALOGE("RING BUFFER WOULD UNDERRUN");
+        return -1;
+    }
+
     if (mOutputDelayRingBufferReadPos + numSamples <= mOutputDelayRingBufferSize
             && (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos
                     || mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) {
@@ -460,10 +467,6 @@
         ALOGV("slow SoftAAC2::outputDelayRingBufferGetSamples()");
 
         for (int32_t i = 0; i < numSamples; i++) {
-            if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
-                ALOGE("RING BUFFER UNDERRUN");
-                return -1;
-            }
             if (samples != 0) {
                 samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos];
             }
@@ -473,22 +476,15 @@
             }
         }
     }
+    mOutputDelayRingBufferFilled -= numSamples;
     return numSamples;
 }
 
 int32_t SoftAAC2::outputDelayRingBufferSamplesAvailable() {
-    int32_t available = mOutputDelayRingBufferWritePos - mOutputDelayRingBufferReadPos;
-    if (available < 0) {
-        available += mOutputDelayRingBufferSize;
-    }
-    if (available < 0) {
-        ALOGE("FATAL RING BUFFER ERROR");
-        return 0;
-    }
-    return available;
+    return mOutputDelayRingBufferFilled;
 }
 
-int32_t SoftAAC2::outputDelayRingBufferSamplesLeft() {
+int32_t SoftAAC2::outputDelayRingBufferSpaceLeft() {
     return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable();
 }
 
@@ -512,6 +508,11 @@
             OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
 
             mEndOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0;
+
+            if (mInputBufferCount == 0 && !(inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
+                ALOGE("first buffer should have OMX_BUFFERFLAG_CODECCONFIG set");
+                inHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
+            }
             if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) {
                 BufferInfo *inInfo = *inQueue.begin();
                 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
@@ -650,7 +651,7 @@
 
             AAC_DECODER_ERROR decoderErr;
             do {
-                if (outputDelayRingBufferSamplesLeft() <
+                if (outputDelayRingBufferSpaceLeft() <
                         (mStreamInfo->frameSize * mStreamInfo->numChannels)) {
                     ALOGV("skipping decode: not enough space left in ringbuffer");
                     break;
@@ -1024,6 +1025,7 @@
     mOutputDelayCompensated = 0;
     mOutputDelayRingBufferWritePos = 0;
     mOutputDelayRingBufferReadPos = 0;
+    mOutputDelayRingBufferFilled = 0;
     mEndOfInput = false;
     mEndOfOutput = false;
     mBufferTimestamps.clear();
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index 9fcb598..c3e4459 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -85,10 +85,11 @@
     short *mOutputDelayRingBuffer;
     int32_t mOutputDelayRingBufferWritePos;
     int32_t mOutputDelayRingBufferReadPos;
+    int32_t mOutputDelayRingBufferFilled;
     bool outputDelayRingBufferPutSamples(INT_PCM *samples, int numSamples);
     int32_t outputDelayRingBufferGetSamples(INT_PCM *samples, int numSamples);
     int32_t outputDelayRingBufferSamplesAvailable();
-    int32_t outputDelayRingBufferSamplesLeft();
+    int32_t outputDelayRingBufferSpaceLeft();
 
     DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2);
 };
diff --git a/media/libstagefright/foundation/ABuffer.cpp b/media/libstagefright/foundation/ABuffer.cpp
index c93c7e8..b214870 100644
--- a/media/libstagefright/foundation/ABuffer.cpp
+++ b/media/libstagefright/foundation/ABuffer.cpp
@@ -19,11 +19,13 @@
 #include "ADebug.h"
 #include "ALooper.h"
 #include "AMessage.h"
+#include "MediaBufferBase.h"
 
 namespace android {
 
 ABuffer::ABuffer(size_t capacity)
-    : mData(malloc(capacity)),
+    : mMediaBufferBase(NULL),
+      mData(malloc(capacity)),
       mCapacity(capacity),
       mRangeOffset(0),
       mRangeLength(capacity),
@@ -32,7 +34,8 @@
 }
 
 ABuffer::ABuffer(void *data, size_t capacity)
-    : mData(data),
+    : mMediaBufferBase(NULL),
+      mData(data),
       mCapacity(capacity),
       mRangeOffset(0),
       mRangeLength(capacity),
@@ -59,6 +62,8 @@
     if (mFarewell != NULL) {
         mFarewell->post();
     }
+
+    setMediaBufferBase(NULL);
 }
 
 void ABuffer::setRange(size_t offset, size_t size) {
@@ -80,5 +85,19 @@
     return mMeta;
 }
 
+MediaBufferBase *ABuffer::getMediaBufferBase() {
+    if (mMediaBufferBase != NULL) {
+        mMediaBufferBase->add_ref();
+    }
+    return mMediaBufferBase;
+}
+
+void ABuffer::setMediaBufferBase(MediaBufferBase *mediaBuffer) {
+    if (mMediaBufferBase != NULL) {
+        mMediaBufferBase->release();
+    }
+    mMediaBufferBase = mediaBuffer;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index b465566..a289637 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -63,6 +63,7 @@
       mSwapMask(0),
       mCheckBandwidthGeneration(0),
       mSwitchGeneration(0),
+      mSubtitleGeneration(0),
       mLastDequeuedTimeUs(0ll),
       mRealTimeBaseUs(0ll),
       mReconfigurationInProgress(false),
@@ -289,6 +290,11 @@
             mLastDequeuedTimeUs = timeUs;
             mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
         } else if (stream == STREAMTYPE_SUBTITLES) {
+            int32_t subtitleGeneration;
+            if ((*accessUnit)->meta()->findInt32("subtitleGeneration", &subtitleGeneration)
+                    && subtitleGeneration != mSubtitleGeneration) {
+               return -EAGAIN;
+            };
             (*accessUnit)->meta()->setInt32(
                     "trackIndex", mPlaylist->getSelectedIndex());
             (*accessUnit)->meta()->setInt64("baseUs", mRealTimeBaseUs);
@@ -759,7 +765,7 @@
     notify->setInt32("switchGeneration", mSwitchGeneration);
 
     FetcherInfo info;
-    info.mFetcher = new PlaylistFetcher(notify, this, uri);
+    info.mFetcher = new PlaylistFetcher(notify, this, uri, mSubtitleGeneration);
     info.mDurationUs = -1ll;
     info.mIsPrepared = false;
     info.mToBeRemoved = false;
@@ -1065,6 +1071,24 @@
     return index;
 }
 
+int64_t LiveSession::latestMediaSegmentStartTimeUs() {
+    sp<AMessage> audioMeta = mPacketSources.valueFor(STREAMTYPE_AUDIO)->getLatestDequeuedMeta();
+    int64_t minSegmentStartTimeUs = -1, videoSegmentStartTimeUs = -1;
+    if (audioMeta != NULL) {
+        audioMeta->findInt64("segmentStartTimeUs", &minSegmentStartTimeUs);
+    }
+
+    sp<AMessage> videoMeta = mPacketSources.valueFor(STREAMTYPE_VIDEO)->getLatestDequeuedMeta();
+    if (videoMeta != NULL
+            && videoMeta->findInt64("segmentStartTimeUs", &videoSegmentStartTimeUs)) {
+        if (minSegmentStartTimeUs < 0 || videoSegmentStartTimeUs < minSegmentStartTimeUs) {
+            minSegmentStartTimeUs = videoSegmentStartTimeUs;
+        }
+
+    }
+    return minSegmentStartTimeUs;
+}
+
 status_t LiveSession::onSeek(const sp<AMessage> &msg) {
     int64_t timeUs;
     CHECK(msg->findInt64("timeUs", &timeUs));
@@ -1117,6 +1141,11 @@
 }
 
 status_t LiveSession::selectTrack(size_t index, bool select) {
+    if (mPlaylist == NULL) {
+        return INVALID_OPERATION;
+    }
+
+    ++mSubtitleGeneration;
     status_t err = mPlaylist->selectTrack(index, select);
     if (err == OK) {
         sp<AMessage> msg = new AMessage(kWhatChangeConfiguration, id());
@@ -1399,6 +1428,10 @@
         int32_t discontinuitySeq = -1;
         sp<AnotherPacketSource> sources[kMaxStreams];
 
+        if (i == kSubtitleIndex) {
+            segmentStartTimeUs = latestMediaSegmentStartTimeUs();
+        }
+
         // TRICKY: looping from i as earlier streams are already removed from streamMask
         for (size_t j = i; j < kMaxStreams; ++j) {
             const AString &streamUri = switching ? mStreams[j].mNewUri : mStreams[j].mUri;
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 6be86cf..7aacca6 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -189,6 +189,7 @@
 
     int32_t mCheckBandwidthGeneration;
     int32_t mSwitchGeneration;
+    int32_t mSubtitleGeneration;
 
     size_t mContinuationCounter;
     sp<AMessage> mContinuation;
@@ -240,6 +241,7 @@
             const char *url, uint8_t *curPlaylistHash, bool *unchanged);
 
     size_t getBandwidthIndex();
+    int64_t latestMediaSegmentStartTimeUs();
 
     static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
     static StreamType indexToType(int idx);
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index f78f8b4..30fa868 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -55,7 +55,8 @@
 PlaylistFetcher::PlaylistFetcher(
         const sp<AMessage> &notify,
         const sp<LiveSession> &session,
-        const char *uri)
+        const char *uri,
+        int32_t subtitleGeneration)
     : mNotify(notify),
       mStartTimeUsNotify(notify->dup()),
       mSession(session),
@@ -73,6 +74,7 @@
       mPrepared(false),
       mNextPTSTimeUs(-1ll),
       mMonitorQueueGeneration(0),
+      mSubtitleGeneration(subtitleGeneration),
       mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY),
       mFirstPTSValid(false),
       mAbsoluteTimeAnchorUs(0ll),
@@ -1407,6 +1409,7 @@
         buffer->meta()->setInt64("durationUs", durationUs);
         buffer->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber));
         buffer->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq);
+        buffer->meta()->setInt32("subtitleGeneration", mSubtitleGeneration);
 
         packetSource->queueAccessUnit(buffer);
         return OK;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 4b09df1..78c358f 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -49,7 +49,8 @@
     PlaylistFetcher(
             const sp<AMessage> &notify,
             const sp<LiveSession> &session,
-            const char *uri);
+            const char *uri,
+            int32_t subtitleGeneration);
 
     sp<DataSource> getDataSource();
 
@@ -133,6 +134,7 @@
     int64_t mNextPTSTimeUs;
 
     int32_t mMonitorQueueGeneration;
+    const int32_t mSubtitleGeneration;
 
     enum RefreshState {
         INITIAL_MINIMUM_RELOAD_DELAY,
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index c74c3e7..a03f6f9 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -254,11 +254,6 @@
             int32_t oldDiscontinuityType;
             if (!oldBuffer->meta()->findInt32(
                         "discontinuity", &oldDiscontinuityType)) {
-                MediaBuffer *mbuf = NULL;
-                oldBuffer->meta()->findPointer("mediaBuffer", (void**)&mbuf);
-                if (mbuf != NULL) {
-                    mbuf->release();
-                }
                 it = mBuffers.erase(it);
                 continue;
             }
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index f3dfc59..423a420 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -254,7 +254,9 @@
     static void addSDES(int s, const sp<ABuffer> &buffer) {
         struct sockaddr_in addr;
         socklen_t addrSize = sizeof(addr);
-        CHECK_EQ(0, getsockname(s, (sockaddr *)&addr, &addrSize));
+        if (getsockname(s, (sockaddr *)&addr, &addrSize) != 0) {
+            inet_aton("0.0.0.0", &(addr.sin_addr));
+        }
 
         uint8_t *data = buffer->data() + buffer->size();
         data[0] = 0x80 | 1;
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index 753b3ec..2834a66 100644
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -74,19 +74,6 @@
     }
 }
 
-static void ReleaseMediaBufferReference(const sp<ABuffer> &accessUnit) {
-    void *mbuf;
-    if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
-            && mbuf != NULL) {
-        ALOGV("releasing mbuf %p", mbuf);
-
-        accessUnit->meta()->setPointer("mediaBuffer", NULL);
-
-        static_cast<MediaBuffer *>(mbuf)->release();
-        mbuf = NULL;
-    }
-}
-
 void Converter::releaseEncoder() {
     if (mEncoder == NULL) {
         return;
@@ -95,18 +82,7 @@
     mEncoder->release();
     mEncoder.clear();
 
-    while (!mInputBufferQueue.empty()) {
-        sp<ABuffer> accessUnit = *mInputBufferQueue.begin();
-        mInputBufferQueue.erase(mInputBufferQueue.begin());
-
-        ReleaseMediaBufferReference(accessUnit);
-    }
-
-    for (size_t i = 0; i < mEncoderInputBuffers.size(); ++i) {
-        sp<ABuffer> accessUnit = mEncoderInputBuffers.itemAt(i);
-        ReleaseMediaBufferReference(accessUnit);
-    }
-
+    mInputBufferQueue.clear();
     mEncoderInputBuffers.clear();
     mEncoderOutputBuffers.clear();
 }
@@ -328,7 +304,7 @@
                     sp<ABuffer> accessUnit;
                     CHECK(msg->findBuffer("accessUnit", &accessUnit));
 
-                    ReleaseMediaBufferReference(accessUnit);
+                    accessUnit->setMediaBufferBase(NULL);
                 }
                 break;
             }
@@ -351,15 +327,16 @@
                         ALOGI("dropping frame.");
                     }
 
-                    ReleaseMediaBufferReference(accessUnit);
+                    accessUnit->setMediaBufferBase(NULL);
                     break;
                 }
 
 #if 0
-                void *mbuf;
-                if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
-                        && mbuf != NULL) {
+                MediaBuffer *mbuf =
+                    (MediaBuffer *)(accessUnit->getMediaBufferBase());
+                if (mbuf != NULL) {
                     ALOGI("queueing mbuf %p", mbuf);
+                    mbuf->release();
                 }
 #endif
 
@@ -647,13 +624,13 @@
                    buffer->data(),
                    buffer->size());
 
-            void *mediaBuffer;
-            if (buffer->meta()->findPointer("mediaBuffer", &mediaBuffer)
-                    && mediaBuffer != NULL) {
-                mEncoderInputBuffers.itemAt(bufferIndex)->meta()
-                    ->setPointer("mediaBuffer", mediaBuffer);
+            MediaBuffer *mediaBuffer =
+                (MediaBuffer *)(buffer->getMediaBufferBase());
+            if (mediaBuffer != NULL) {
+                mEncoderInputBuffers.itemAt(bufferIndex)->setMediaBufferBase(
+                        mediaBuffer);
 
-                buffer->meta()->setPointer("mediaBuffer", NULL);
+                buffer->setMediaBufferBase(NULL);
             }
         } else {
             flags = MediaCodec::BUFFER_FLAG_EOS;
diff --git a/media/libstagefright/wifi-display/source/MediaPuller.cpp b/media/libstagefright/wifi-display/source/MediaPuller.cpp
index 7e8891d..86b918f 100644
--- a/media/libstagefright/wifi-display/source/MediaPuller.cpp
+++ b/media/libstagefright/wifi-display/source/MediaPuller.cpp
@@ -179,7 +179,7 @@
                 } else {
                     // video encoder will release MediaBuffer when done
                     // with underlying data.
-                    accessUnit->meta()->setPointer("mediaBuffer", mbuf);
+                    accessUnit->setMediaBufferBase(mbuf);
                 }
 
                 sp<AMessage> notify = mNotify->dup();
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index 95ac070..d51ee8e 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -112,7 +112,7 @@
     STRING_TO_ENUM(AUDIO_DEVICE_IN_LOOPBACK),
 };
 
-const StringToEnum sFlagNameToEnumTable[] = {
+const StringToEnum sOutputFlagNameToEnumTable[] = {
     STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT),
     STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY),
     STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_FAST),
@@ -122,6 +122,11 @@
     STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_HW_AV_SYNC),
 };
 
+const StringToEnum sInputFlagNameToEnumTable[] = {
+    STRING_TO_ENUM(AUDIO_INPUT_FLAG_FAST),
+    STRING_TO_ENUM(AUDIO_INPUT_FLAG_HW_HOTWORD),
+};
+
 const StringToEnum sFormatNameToEnumTable[] = {
     STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT),
     STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_BIT),
@@ -1292,16 +1297,41 @@
         break;
     }
 
+    audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
+    bool isSoundTrigger = false;
+    audio_source_t halInputSource = inputSource;
+    if (inputSource == AUDIO_SOURCE_HOTWORD) {
+        ssize_t index = mSoundTriggerSessions.indexOfKey(session);
+        if (index >= 0) {
+            input = mSoundTriggerSessions.valueFor(session);
+            isSoundTrigger = true;
+            flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_HW_HOTWORD);
+            ALOGV("SoundTrigger capture on session %d input %d", session, input);
+        } else {
+            halInputSource = AUDIO_SOURCE_VOICE_RECOGNITION;
+        }
+    }
+
     sp<IOProfile> profile = getInputProfile(device,
                                          samplingRate,
                                          format,
                                          channelMask,
                                          flags);
     if (profile == 0) {
-        ALOGW("getInput() could not find profile for device 0x%X, samplingRate %u, format %#x, "
-                "channelMask 0x%X, flags %#x",
-                device, samplingRate, format, channelMask, flags);
-        return AUDIO_IO_HANDLE_NONE;
+        //retry without flags
+        audio_input_flags_t log_flags = flags;
+        flags = AUDIO_INPUT_FLAG_NONE;
+        profile = getInputProfile(device,
+                                 samplingRate,
+                                 format,
+                                 channelMask,
+                                 flags);
+        if (profile == 0) {
+            ALOGW("getInput() could not find profile for device 0x%X, samplingRate %u, format %#x, "
+                    "channelMask 0x%X, flags %#x",
+                    device, samplingRate, format, channelMask, log_flags);
+            return AUDIO_IO_HANDLE_NONE;
+        }
     }
 
     if (profile->mModule->mHandle == 0) {
@@ -1313,20 +1343,7 @@
     config.sample_rate = samplingRate;
     config.channel_mask = channelMask;
     config.format = format;
-    audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
 
-    bool isSoundTrigger = false;
-    audio_source_t halInputSource = inputSource;
-    if (inputSource == AUDIO_SOURCE_HOTWORD) {
-        ssize_t index = mSoundTriggerSessions.indexOfKey(session);
-        if (index >= 0) {
-            input = mSoundTriggerSessions.valueFor(session);
-            isSoundTrigger = true;
-            ALOGV("SoundTrigger capture on session %d input %d", session, input);
-        } else {
-            halInputSource = AUDIO_SOURCE_VOICE_RECOGNITION;
-        }
-    }
     status_t status = mpClientInterface->openInput(profile->mModule->mHandle,
                                                    &input,
                                                    &config,
@@ -5220,7 +5237,7 @@
         mStrategyMutedByDevice[i] = false;
     }
     if (profile != NULL) {
-        mFlags = profile->mFlags;
+        mFlags = (audio_output_flags_t)profile->mFlags;
         mSamplingRate = profile->pickSamplingRate();
         mFormat = profile->pickFormat();
         mChannelMask = profile->pickChannelMask();
@@ -5568,6 +5585,8 @@
         } else if (strcmp(node->name, DEVICES_TAG) == 0) {
             profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
                                                            mDeclaredDevices);
+        } else if (strcmp(node->name, FLAGS_TAG) == 0) {
+            profile->mFlags = parseInputFlagNames((char *)node->value);
         } else if (strcmp(node->name, GAINS_TAG) == 0) {
             profile->loadGains(node);
         }
@@ -5613,7 +5632,7 @@
             profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
                                                            mDeclaredDevices);
         } else if (strcmp(node->name, FLAGS_TAG) == 0) {
-            profile->mFlags = parseFlagNames((char *)node->value);
+            profile->mFlags = parseOutputFlagNames((char *)node->value);
         } else if (strcmp(node->name, GAINS_TAG) == 0) {
             profile->loadGains(node);
         }
@@ -5728,7 +5747,7 @@
 
 AudioPolicyManager::AudioPort::AudioPort(const String8& name, audio_port_type_t type,
           audio_port_role_t role, const sp<HwModule>& module) :
-    mName(name), mType(type), mRole(role), mModule(module), mFlags((audio_output_flags_t)0)
+    mName(name), mType(type), mRole(role), mModule(module), mFlags(0)
 {
     mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) ||
                     ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK));
@@ -6560,7 +6579,7 @@
                                                             uint32_t *updatedSamplingRate,
                                                             audio_format_t format,
                                                             audio_channel_mask_t channelMask,
-                                                            audio_output_flags_t flags) const
+                                                            uint32_t flags) const
 {
     const bool isPlaybackThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SOURCE;
     const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK;
@@ -6602,7 +6621,7 @@
     // An existing fast stream is compatible with a normal track request.
     // An existing normal stream is compatible with a fast track request,
     // but the fast request will be denied by AudioFlinger and converted to normal track.
-    if (isRecordThread && (((audio_input_flags_t) mFlags ^ (audio_input_flags_t) flags) &
+    if (isRecordThread && ((mFlags ^ flags) &
             ~AUDIO_INPUT_FLAG_FAST)) {
         return false;
     }
@@ -6958,7 +6977,7 @@
 
 // --- audio_policy.conf file parsing
 
-audio_output_flags_t AudioPolicyManager::parseFlagNames(char *name)
+uint32_t AudioPolicyManager::parseOutputFlagNames(char *name)
 {
     uint32_t flag = 0;
 
@@ -6967,8 +6986,8 @@
     char *flagName = strtok(name, "|");
     while (flagName != NULL) {
         if (strlen(flagName) != 0) {
-            flag |= stringToEnum(sFlagNameToEnumTable,
-                               ARRAY_SIZE(sFlagNameToEnumTable),
+            flag |= stringToEnum(sOutputFlagNameToEnumTable,
+                               ARRAY_SIZE(sOutputFlagNameToEnumTable),
                                flagName);
         }
         flagName = strtok(NULL, "|");
@@ -6980,7 +6999,25 @@
         flag |= AUDIO_OUTPUT_FLAG_DIRECT;
     }
 
-    return (audio_output_flags_t)flag;
+    return flag;
+}
+
+uint32_t AudioPolicyManager::parseInputFlagNames(char *name)
+{
+    uint32_t flag = 0;
+
+    // it is OK to cast name to non const here as we are not going to use it after
+    // strtok() modifies it
+    char *flagName = strtok(name, "|");
+    while (flagName != NULL) {
+        if (strlen(flagName) != 0) {
+            flag |= stringToEnum(sInputFlagNameToEnumTable,
+                               ARRAY_SIZE(sInputFlagNameToEnumTable),
+                               flagName);
+        }
+        flagName = strtok(NULL, "|");
+    }
+    return flag;
 }
 
 audio_devices_t AudioPolicyManager::parseDeviceNames(char *name)
diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h
index da0d95d..0ea7b97 100644
--- a/services/audiopolicy/AudioPolicyManager.h
+++ b/services/audiopolicy/AudioPolicyManager.h
@@ -283,8 +283,8 @@
             Vector <audio_format_t> mFormats; // supported audio formats
             Vector < sp<AudioGain> > mGains; // gain controllers
             sp<HwModule> mModule;                 // audio HW module exposing this I/O stream
-            audio_output_flags_t mFlags; // attribute flags (e.g primary output,
-                                                // direct output...). For outputs only.
+            uint32_t mFlags; // attribute flags (e.g primary output,
+                                                // direct output...).
         };
 
         class AudioPortConfig: public virtual RefBase
@@ -387,7 +387,7 @@
                                      uint32_t *updatedSamplingRate,
                                      audio_format_t format,
                                      audio_channel_mask_t channelMask,
-                                     audio_output_flags_t flags) const;
+                                     uint32_t flags) const;
 
             void dump(int fd);
             void log();
@@ -754,7 +754,8 @@
                                       size_t size,
                                       uint32_t value);
         static bool stringToBool(const char *value);
-        static audio_output_flags_t parseFlagNames(char *name);
+        static uint32_t parseOutputFlagNames(char *name);
+        static uint32_t parseInputFlagNames(char *name);
         static audio_devices_t parseDeviceNames(char *name);
         void loadHwModule(cnode *root);
         void loadHwModules(cnode *root);
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index fe2f299..48ec730 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -1217,6 +1217,8 @@
     {
         SharedParameters::Lock l(mParameters);
         if (l.mParameters.state < Parameters::PREVIEW) {
+            ALOGE("%s: Camera %d: Call autoFocus when preview is inactive (state = %d).",
+                    __FUNCTION__, mCameraId, l.mParameters.state);
             return INVALID_OPERATION;
         }
 
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
index de31e23..f110b66 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
@@ -573,6 +573,11 @@
                     continue;
                 }
                 uint8_t afMode = entry.data.u8[0];
+                if (afMode == ANDROID_CONTROL_AF_MODE_OFF) {
+                    // Skip all the ZSL buffer for manual AF mode, as we don't really
+                    // know the af state.
+                    continue;
+                }
 
                 // Check AF state if device has focuser and focus mode isn't fixed
                 if (mHasFocuser && !isFixedFocusMode(afMode)) {
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 80c797a..e3301aa 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -512,12 +512,24 @@
 
 status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {
     String8 result;
-    result.appendFormat("CameraDeviceClient[%d] (%p) PID: %d, dump:\n",
+    result.appendFormat("CameraDeviceClient[%d] (%p) dump:\n",
             mCameraId,
-            getRemoteCallback()->asBinder().get(),
-            mClientPid);
-    result.append("  State: ");
+            getRemoteCallback()->asBinder().get());
+    result.appendFormat("  Current client: %s (PID %d, UID %u)\n",
+            String8(mClientPackageName).string(),
+            mClientPid, mClientUid);
 
+    result.append("  State:\n");
+    result.appendFormat("    Request ID counter: %d\n", mRequestIdCounter);
+    if (!mStreamMap.isEmpty()) {
+        result.append("    Current stream IDs:\n");
+        for (size_t i = 0; i < mStreamMap.size(); i++) {
+            result.appendFormat("      Stream %d\n", mStreamMap.valueAt(i));
+        }
+    } else {
+        result.append("    No streams configured.\n");
+    }
+    write(fd, result.string(), result.size());
     // TODO: print dynamic/request section from most recent requests
     mFrameProcessor->dump(fd, args);
 
diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
index f8823a3..2ea460f 100644
--- a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
+++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
@@ -336,11 +336,11 @@
             mCameraId,
             getRemoteCallback()->asBinder().get(),
             mClientPid);
-    result.append("  State: ");
+    result.append("  State:\n");
+    write(fd, result.string(), result.size());
 
     // TODO: print dynamic/request section from most recent requests
     mFrameProcessor->dump(fd, args);
-
     return dumpDevice(fd, args);
 }
 
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 24d173c..d6db151 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -57,8 +57,10 @@
         mSharedCameraCallbacks(remoteCallback),
         mDeviceVersion(cameraService->getDeviceVersion(cameraId))
 {
-    ALOGI("Camera %d: Opened", cameraId);
+    ALOGI("Camera %d: Opened. Client: %s (PID %d, UID %d)", cameraId,
+            String8(clientPackageName).string(), clientPid, clientUid);
 
+    mInitialClientPid = clientPid;
     mDevice = CameraDeviceFactory::createDevice(cameraId);
     LOG_ALWAYS_FATAL_IF(mDevice == 0, "Device should never be NULL here.");
 }
@@ -114,7 +116,10 @@
 
     disconnect();
 
-    ALOGI("Closed Camera %d", TClientBase::mCameraId);
+    ALOGI("Closed Camera %d. Client was: %s (PID %d, UID %u)",
+            TClientBase::mCameraId,
+            String8(TClientBase::mClientPackageName).string(),
+            mInitialClientPid, TClientBase::mClientUid);
 }
 
 template <typename TClientBase>
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index f57d204..d198e4e 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -107,6 +107,9 @@
 
 protected:
 
+    // The PID provided in the constructor call
+    pid_t mInitialClientPid;
+
     virtual sp<IBinder> asBinderWrapper() {
         return IInterface::asBinder();
     }