Merge "MediaCodec: Fix deallocated pointer reference when calling reset()" 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/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..dad480d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -643,6 +643,13 @@
             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;
         }
@@ -1270,8 +1277,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 +1298,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 +1421,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 +1470,7 @@
         mCCDecoder->decode(accessUnit);
     }
 
-    if (mAggregateBuffer != NULL) {
+    if (doBufferAggregation && (mAggregateBuffer != NULL)) {
         ALOGV("feedDecoderInputData() reply with aggregated buffer, %zu",
                 mAggregateBuffer->size());
         reply->setBuffer("buffer", mAggregateBuffer);
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/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 7674616..a8c8818 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -26,6 +26,8 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 
+#include <VideoFrameScheduler.h>
+
 #include <inttypes.h>
 
 namespace android {
@@ -137,6 +139,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,6 +245,14 @@
             break;
         }
 
+        case kWhatSetVideoFrameRate:
+        {
+            float fps;
+            CHECK(msg->findFloat("frame-rate", &fps));
+            onSetVideoFrameRate(fps);
+            break;
+        }
+
         case kWhatAudioOffloadTearDown:
         {
             onAudioOffloadTearDown();
@@ -502,16 +518,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 +540,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 +611,7 @@
         mVideoLateByUs = 0ll;
     }
 
+    entry->mNotifyConsumed->setInt64("timestampNs", realTimeUs * 1000ll);
     entry->mNotifyConsumed->setInt32("render", !tooLate);
     entry->mNotifyConsumed->post();
     mVideoQueue.erase(mVideoQueue.begin());
@@ -630,6 +654,10 @@
         mHasAudio = true;
     } else {
         mHasVideo = true;
+        if (mVideoScheduler == NULL) {
+            mVideoScheduler = new VideoFrameScheduler();
+            mVideoScheduler->init();
+        }
     }
 
     if (dropBufferWhileFlushing(audio, msg)) {
@@ -795,6 +823,10 @@
         mDrainVideoQueuePending = false;
         ++mVideoQueueGeneration;
 
+        if (mVideoScheduler != NULL) {
+            mVideoScheduler->restart();
+        }
+
         prepareForMediaRenderingStart();
     }
 
@@ -946,6 +978,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.
 //
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 97fdae7..e28071f 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',
@@ -82,6 +85,7 @@
         kWhatResume              = 'resm',
         kWhatStopAudioSink       = 'stpA',
         kWhatDisableOffloadAudio = 'noOA',
+        kWhatSetVideoFrameRate   = 'sVFR',
     };
 
     struct QueueEntry {
@@ -100,6 +104,7 @@
     List<QueueEntry> mAudioQueue;
     List<QueueEntry> mVideoQueue;
     uint32_t mNumFramesWritten;
+    sp<VideoFrameScheduler> mVideoScheduler;
 
     bool mDrainAudioQueuePending;
     bool mDrainVideoQueuePending;
@@ -147,6 +152,7 @@
     void onDisableOffloadAudio();
     void onPause();
     void onResume();
+    void onSetVideoFrameRate(float fps);
     void onAudioOffloadTearDown();
 
     void notifyEOS(bool audio, status_t finalResult, int64_t delayUs = 0);
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/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/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/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();
     }