Merge "NuPlayer: don't restart AudioSink when torn down due to pause timeout." into lmp-dev
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index da4c20c..28e5c56 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -77,8 +77,14 @@
     };
 
     static bool isFlexibleColorFormat(
-        const sp<IOMX> &omx, IOMX::node_id node,
-        uint32_t colorFormat, OMX_U32 *flexibleEquivalent);
+            const sp<IOMX> &omx, IOMX::node_id node,
+            uint32_t colorFormat, OMX_U32 *flexibleEquivalent);
+
+    // Returns 0 if configuration is not supported.  NOTE: this is treated by
+    // some OMX components as auto level, and by others as invalid level.
+    static int /* OMX_VIDEO_AVCLEVELTYPE */ getAVCLevelFor(
+            int width, int height, int rate, int bitrate,
+            OMX_VIDEO_AVCPROFILETYPE profile = OMX_VIDEO_AVCProfileBaseline);
 
 protected:
     virtual ~ACodec();
@@ -267,7 +273,8 @@
             bool encoder,
             int32_t numChannels, int32_t sampleRate, int32_t bitRate,
             int32_t aacProfile, bool isADTS, int32_t sbrMode,
-            int32_t maxOutputChannelCount, const drcParams_t& drc);
+            int32_t maxOutputChannelCount, const drcParams_t& drc,
+            int32_t pcmLimiterEnable);
 
     status_t setupAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate);
 
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 8000e84..3630263 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -34,6 +34,7 @@
 struct AString;
 struct IMediaHTTPService;
 class String8;
+struct HTTPBase;
 
 class DataSource : public RefBase {
 public:
@@ -48,7 +49,10 @@
             const sp<IMediaHTTPService> &httpService,
             const char *uri,
             const KeyedVector<String8, String8> *headers = NULL,
-            String8 *contentType = NULL);
+            String8 *contentType = NULL,
+            HTTPBase *httpSource = NULL);
+
+    static sp<DataSource> CreateMediaHTTP(const sp<IMediaHTTPService> &httpService);
 
     DataSource() {}
 
diff --git a/include/media/stagefright/foundation/AUtils.h b/include/media/stagefright/foundation/AUtils.h
new file mode 100644
index 0000000..3a73a39
--- /dev/null
+++ b/include/media/stagefright/foundation/AUtils.h
@@ -0,0 +1,65 @@
+/*
+ * 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 A_UTILS_H_
+
+#define A_UTILS_H_
+
+/* ============================ math templates ============================ */
+
+/* T must be integer type, den must not be 0 */
+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;
+    }
+}
+
+/* == ceil(nom / den). T must be integer type, den must not be 0 */
+template<class T>
+inline static const T divUp(const T &nom, const T &den) {
+    if (den < 0) {
+        return (nom < 0 ? nom + den + 1 : nom) / den;
+    } else {
+        return (nom < 0 ? nom : nom + den - 1) / 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;
+}
+
+/* T must be integer type, period must be positive */
+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);
+}
+
+#endif  // A_UTILS_H_
diff --git a/media/img_utils/src/DngUtils.cpp b/media/img_utils/src/DngUtils.cpp
index 14b31ec..d3b4a35 100644
--- a/media/img_utils/src/DngUtils.cpp
+++ b/media/img_utils/src/DngUtils.cpp
@@ -229,7 +229,9 @@
     err = mEndianOut.write(version, 0, NELEMS(version));
     if (err != OK) return err;
 
-    uint32_t flags = FLAG_OPTIONAL | FLAG_OPTIONAL_FOR_PREVIEW;
+    // Do not include optional flag for preview, as this can have a large effect on the output.
+    uint32_t flags = FLAG_OPTIONAL;
+
     err = mEndianOut.write(&flags, 0, 1);
     if (err != OK) return err;
 
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index b904aa8..17190fb 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -30,6 +30,7 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/ACodec.h>
 #include <media/stagefright/AudioSource.h>
 #include <media/stagefright/AMRWriter.h>
 #include <media/stagefright/AACWriter.h>
@@ -1243,6 +1244,10 @@
         if (videoCodec == VIDEO_ENCODER_H264) {
             ALOGI("Force to use AVC baseline profile");
             setParamVideoEncoderProfile(OMX_VIDEO_AVCProfileBaseline);
+            // set 0 for invalid levels - this will be rejected by the
+            // codec if it cannot handle it during configure
+            setParamVideoEncoderLevel(ACodec::getAVCLevelFor(
+                    videoFrameWidth, videoFrameHeight, videoFrameRate, videoBitRate));
         }
     }
 }
diff --git a/media/libmediaplayerservice/VideoFrameScheduler.cpp b/media/libmediaplayerservice/VideoFrameScheduler.cpp
index 1a5f3e0..ce5f5fe 100644
--- a/media/libmediaplayerservice/VideoFrameScheduler.cpp
+++ b/media/libmediaplayerservice/VideoFrameScheduler.cpp
@@ -27,6 +27,7 @@
 #include <ui/DisplayStatInfo.h>
 
 #include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AUtils.h>
 
 #include "VideoFrameScheduler.h"
 
@@ -35,36 +36,6 @@
 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;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index f84decd..6859a1a 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -36,6 +36,7 @@
 #include "../../libstagefright/include/DRMExtractor.h"
 #include "../../libstagefright/include/NuCachedSource2.h"
 #include "../../libstagefright/include/WVMExtractor.h"
+#include "../../libstagefright/include/HTTPBase.h"
 
 namespace android {
 
@@ -64,6 +65,7 @@
     mAudioTimeUs = 0;
     mVideoTimeUs = 0;
     mHTTPService.clear();
+    mHttpSource.clear();
     mUri.clear();
     mUriHeaders.clear();
     mFd = -1;
@@ -73,6 +75,7 @@
     mDecryptHandle = NULL;
     mDrmManagerClient = NULL;
     mStarted = false;
+    mStopRead = true;
 }
 
 status_t NuPlayer::GenericSource::setDataSource(
@@ -284,10 +287,23 @@
     // delayed data source creation
     if (mDataSource == NULL) {
         if (!mUri.empty()) {
-            mIsWidevine = !strncasecmp(mUri.c_str(), "widevine://", 11);
+            const char* uri = mUri.c_str();
+            mIsWidevine = !strncasecmp(uri, "widevine://", 11);
+
+            if (!strncasecmp("http://", uri, 7)
+                    || !strncasecmp("https://", uri, 8)
+                    || mIsWidevine) {
+                mHttpSource = DataSource::CreateMediaHTTP(mHTTPService);
+                if (mHttpSource == NULL) {
+                    ALOGE("Failed to create http source!");
+                    notifyPreparedAndCleanup(UNKNOWN_ERROR);
+                    return;
+                }
+            }
 
             mDataSource = DataSource::CreateFromURI(
-                   mHTTPService, mUri.c_str(), &mUriHeaders, &mContentType);
+                   mHTTPService, uri, &mUriHeaders, &mContentType,
+                   static_cast<HTTPBase *>(mHttpSource.get()));
         } else {
             // set to false first, if the extractor
             // comes back as secure, set it to true then.
@@ -360,6 +376,7 @@
         mSniffedMIME = "";
         mDataSource.clear();
         mCachedSource.clear();
+        mHttpSource.clear();
 
         cancelPollBuffering();
     }
@@ -439,6 +456,7 @@
 void NuPlayer::GenericSource::start() {
     ALOGI("start");
 
+    mStopRead = false;
     if (mAudioTrack.mSource != NULL) {
         CHECK_EQ(mAudioTrack.mSource->start(), (status_t)OK);
 
@@ -459,6 +477,12 @@
     // nothing to do, just account for DRM playback status
     setDrmPlaybackStatusIfNeeded(Playback::STOP, 0);
     mStarted = false;
+    if (mIsWidevine) {
+        // For a widevine source we need to prevent any further reads.
+        sp<AMessage> msg = new AMessage(kWhatStopWidevine, id());
+        sp<AMessage> response;
+        (void) msg->postAndAwaitResponse(&response);
+    }
 }
 
 void NuPlayer::GenericSource::pause() {
@@ -479,6 +503,8 @@
         if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
             static_cast<NuCachedSource2 *>(mDataSource.get())->disconnect();
         }
+    } else if (mHttpSource != NULL) {
+        static_cast<HTTPBase *>(mHttpSource.get())->disconnect();
     }
 }
 
@@ -675,6 +701,20 @@
           break;
       }
 
+      case kWhatStopWidevine:
+      {
+          // mStopRead is only used for Widevine to prevent the video source
+          // from being read while the associated video decoder is shutting down.
+          mStopRead = true;
+          if (mVideoTrack.mSource != NULL) {
+              mVideoTrack.mPackets->clear();
+          }
+          sp<AMessage> response = new AMessage;
+          uint32_t replyID;
+          CHECK(msg->senderAwaitsResponse(&replyID));
+          response->postReply(replyID);
+          break;
+      }
       default:
           Source::onMessageReceived(msg);
           break;
@@ -1082,6 +1122,11 @@
 }
 
 status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs) {
+    // If the Widevine source is stopped, do not attempt to read any
+    // more buffers.
+    if (mStopRead) {
+        return INVALID_OPERATION;
+    }
     if (mVideoTrack.mSource != NULL) {
         int64_t actualTimeUs;
         readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, &actualTimeUs);
@@ -1193,6 +1238,10 @@
 
 void NuPlayer::GenericSource::readBuffer(
         media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) {
+    // Do not read data if Widevine source is stopped
+    if (mStopRead) {
+        return;
+    }
     Track *track;
     size_t maxBuffers = 1;
     switch (trackType) {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 24bb6af..f8601ea 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -93,6 +93,7 @@
         kWhatSelectTrack,
         kWhatSeek,
         kWhatReadBuffer,
+        kWhatStopWidevine,
     };
 
     Vector<sp<MediaSource> > mSources;
@@ -126,11 +127,13 @@
 
     sp<DataSource> mDataSource;
     sp<NuCachedSource2> mCachedSource;
+    sp<DataSource> mHttpSource;
     sp<WVMExtractor> mWVMExtractor;
     sp<MetaData> mFileMeta;
     DrmManagerClient *mDrmManagerClient;
     sp<DecryptHandle> mDecryptHandle;
     bool mStarted;
+    bool mStopRead;
     String8 mContentType;
     AString mSniffedMIME;
     off64_t mMetaDataSize;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index ea60c06..5270efc 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -791,6 +791,11 @@
                     ALOGV("initiating %s decoder shutdown",
                          audio ? "audio" : "video");
 
+                    // Widevine source reads must stop before releasing the video decoder.
+                    if (!audio && mSource != NULL && mSourceFlags & Source::FLAG_SECURE) {
+                        mSource->stop();
+                    }
+
                     getDecoder(audio)->initiateShutdown();
 
                     if (audio) {
@@ -833,30 +838,50 @@
                 finishFlushIfPossible();
             } else if (what == Decoder::kWhatError) {
                 status_t err;
-                if (!msg->findInt32("err", &err)) {
+                if (!msg->findInt32("err", &err) || err == OK) {
                     err = UNKNOWN_ERROR;
                 }
-                ALOGE("received error from %s decoder %#x", audio ? "audio" : "video", err);
 
-                ALOGI("shutting down %s", audio ? "audio" : "video");
-                if (audio && mFlushingAudio != NONE) {
-                    mRenderer->queueEOS(audio, err);
-                    mAudioDecoder.clear();
-                    ++mAudioDecoderGeneration;
-                    mFlushingAudio = SHUT_DOWN;
-                    finishFlushIfPossible();
-                } else if (!audio && mFlushingVideo != NONE) {
-                    mRenderer->queueEOS(audio, err);
-                    mVideoDecoder.clear();
-                    ++mVideoDecoderGeneration;
-                    mFlushingVideo = SHUT_DOWN;
-                    finishFlushIfPossible();
-                }  else {
-                    mDeferredActions.push_back(
-                            new ShutdownDecoderAction(audio, !audio /* video */));
-                    processDeferredActions();
-                    notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+                // Decoder errors can be due to Source (e.g. from streaming),
+                // or from decoding corrupted bitstreams, or from other decoder
+                // MediaCodec operations (e.g. from an ongoing reset or seek).
+                //
+                // We try to gracefully shut down the affected decoder if possible,
+                // rather than trying to force the shutdown with something
+                // similar to performReset(). This method can lead to a hang
+                // if MediaCodec functions block after an error, but they should
+                // typically return INVALID_OPERATION instead of blocking.
+
+                FlushStatus *flushing = audio ? &mFlushingAudio : &mFlushingVideo;
+                ALOGE("received error(%#x) from %s decoder, flushing(%d), now shutting down",
+                        err, audio ? "audio" : "video", *flushing);
+
+                switch (*flushing) {
+                    case NONE:
+                        mDeferredActions.push_back(
+                                new ShutdownDecoderAction(audio, !audio /* video */));
+                        processDeferredActions();
+                        break;
+                    case FLUSHING_DECODER:
+                        *flushing = FLUSHING_DECODER_SHUTDOWN; // initiate shutdown after flush.
+                        break; // Wait for flush to complete.
+                    case FLUSHING_DECODER_SHUTDOWN:
+                        break; // Wait for flush to complete.
+                    case SHUTTING_DOWN_DECODER:
+                        break; // Wait for shutdown to complete.
+                    case FLUSHED:
+                        // Widevine source reads must stop before releasing the video decoder.
+                        if (!audio && mSource != NULL && mSourceFlags & Source::FLAG_SECURE) {
+                            mSource->stop();
+                        }
+                        getDecoder(audio)->initiateShutdown(); // In the middle of a seek.
+                        *flushing = SHUTTING_DOWN_DECODER;     // Shut down.
+                        break;
+                    case SHUT_DOWN:
+                        finishFlushIfPossible();  // Should not occur.
+                        break;                    // Finish anyways.
                 }
+                notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
             } else if (what == Decoder::kWhatDrainThisBuffer) {
                 renderBuffer(audio, msg);
             } else {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 1a066b7..27f6131 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -140,6 +140,8 @@
             format, surface, NULL /* crypto */, 0 /* flags */);
     if (err != OK) {
         ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err);
+        mCodec->release();
+        mCodec.clear();
         handleError(err);
         return;
     }
@@ -152,6 +154,8 @@
     err = mCodec->start();
     if (err != OK) {
         ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err);
+        mCodec->release();
+        mCodec.clear();
         handleError(err);
         return;
     }
@@ -185,6 +189,8 @@
     for (size_t i = 0; i < mInputBufferIsDequeued.size(); i++) {
         mInputBufferIsDequeued.editItemAt(i) = false;
     }
+
+    mPendingInputMessages.clear();
 }
 
 void NuPlayer::Decoder::requestCodecNotification() {
@@ -270,7 +276,19 @@
         ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
         reply->setBuffer("buffer", buffer);
         mCSDsToSubmit.removeAt(0);
-        reply->post();
+        CHECK(onInputBufferFilled(reply));
+        return true;
+    }
+
+    while (!mPendingInputMessages.empty()) {
+        sp<AMessage> msg = *mPendingInputMessages.begin();
+        if (!onInputBufferFilled(msg)) {
+            break;
+        }
+        mPendingInputMessages.erase(mPendingInputMessages.begin());
+    }
+
+    if (!mInputBufferIsDequeued.editItemAt(bufferIx)) {
         return true;
     }
 
@@ -282,7 +300,7 @@
     return true;
 }
 
-void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) {
+bool android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) {
     size_t bufferIx;
     CHECK(msg->findSize("buffer-ix", &bufferIx));
     CHECK_LT(bufferIx, mInputBuffers.size());
@@ -302,9 +320,12 @@
                 const sp<ABuffer> &buf = mInputBuffers[ix];
                 if (buf->data() == mediaBuffer->data()) {
                     // all input buffers are dequeued on start, hence the check
-                    CHECK(mInputBufferIsDequeued[ix]);
-                    ALOGV("[%s] received MediaBuffer for #%zu instead of #%zu",
-                            mComponentName.c_str(), ix, bufferIx);
+                    if (!mInputBufferIsDequeued[ix]) {
+                        ALOGV("[%s] received MediaBuffer for #%zu instead of #%zu",
+                                mComponentName.c_str(), ix, bufferIx);
+                        mediaBuffer->release();
+                        return false;
+                    }
 
                     // TRICKY: need buffer for the metadata, so instead, set
                     // codecBuffer to the same (though incorrect) buffer to
@@ -329,7 +350,7 @@
 
         if (streamErr == OK) {
             /* buffers are returned to hold on to */
-            return;
+            return true;
         }
 
         // attempt to queue EOS
@@ -394,6 +415,7 @@
             }
         }
     }
+    return true;
 }
 
 bool NuPlayer::Decoder::handleAnOutputBuffer() {
@@ -511,9 +533,9 @@
     if (err != OK) {
         ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err);
         handleError(err);
-        return;
+        // finish with posting kWhatFlushCompleted.
+        // we attempt to release the buffers even if flush fails.
     }
-
     releaseAndResetMediaBuffers();
 
     sp<AMessage> notify = mNotify->dup();
@@ -551,7 +573,7 @@
     if (err != OK) {
         ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err);
         handleError(err);
-        return;
+        // finish with posting kWhatShutdownCompleted.
     }
 
     sp<AMessage> notify = mNotify->dup();
@@ -600,13 +622,21 @@
         case kWhatCodecNotify:
         {
             if (!isStaleReply(msg)) {
-                if (!mPaused) {
-                    while (handleAnInputBuffer()) {
-                    }
+                int32_t numInput, numOutput;
+
+                if (!msg->findInt32("input-buffers", &numInput)) {
+                    numInput = INT32_MAX;
                 }
 
-                while (handleAnOutputBuffer()) {
+                if (!msg->findInt32("output-buffers", &numOutput)) {
+                    numOutput = INT32_MAX;
                 }
+
+                if (!mPaused) {
+                    while (numInput-- > 0 && handleAnInputBuffer()) {}
+                }
+
+                while (numOutput-- > 0 && handleAnOutputBuffer()) {}
             }
 
             requestCodecNotification();
@@ -616,7 +646,10 @@
         case kWhatInputBufferFilled:
         {
             if (!isStaleReply(msg)) {
-                onInputBufferFilled(msg);
+                if (!mPendingInputMessages.empty()
+                        || !onInputBufferFilled(msg)) {
+                    mPendingInputMessages.push_back(msg);
+                }
             }
 
             break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index cc1bdff..dba3eee 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -80,6 +80,8 @@
     sp<ALooper> mCodecLooper;
     sp<ALooper> mDecoderLooper;
 
+    List<sp<AMessage> > mPendingInputMessages;
+
     Vector<sp<ABuffer> > mInputBuffers;
     Vector<sp<ABuffer> > mOutputBuffers;
     Vector<sp<ABuffer> > mCSDsForCurrentFormat;
@@ -98,7 +100,7 @@
     void onConfigure(const sp<AMessage> &format);
     void onFlush();
     void onResume();
-    void onInputBufferFilled(const sp<AMessage> &msg);
+    bool onInputBufferFilled(const sp<AMessage> &msg);
     void onRenderBuffer(const sp<AMessage> &msg);
     void onShutdown();
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index a9bca49..1a01d52 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -27,6 +27,7 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
 
 namespace android {
 
@@ -47,6 +48,7 @@
       mLooping(false),
       mAutoLoop(false),
       mStartupSeekTimeUs(-1) {
+    ALOGV("NuPlayerDriver(%p)", this);
     mLooper->setName("NuPlayerDriver Looper");
 
     mLooper->start(
@@ -61,6 +63,7 @@
 }
 
 NuPlayerDriver::~NuPlayerDriver() {
+    ALOGV("~NuPlayerDriver(%p)", this);
     mLooper->stop();
 }
 
@@ -78,9 +81,9 @@
         const sp<IMediaHTTPService> &httpService,
         const char *url,
         const KeyedVector<String8, String8> *headers) {
+    ALOGV("setDataSource(%p) url(%s)", this, uriDebugString(url, false).c_str());
     Mutex::Autolock autoLock(mLock);
 
-    ALOGV("setDataSource: url=%s", url);
     if (mState != STATE_IDLE) {
         return INVALID_OPERATION;
     }
@@ -97,9 +100,9 @@
 }
 
 status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
+    ALOGV("setDataSource(%p) file(%d)", this, fd);
     Mutex::Autolock autoLock(mLock);
 
-    ALOGV("setDataSource: fd=%d", fd);
     if (mState != STATE_IDLE) {
         return INVALID_OPERATION;
     }
@@ -116,9 +119,9 @@
 }
 
 status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) {
+    ALOGV("setDataSource(%p) stream source", this);
     Mutex::Autolock autoLock(mLock);
 
-    ALOGV("setDataSource: stream source");
     if (mState != STATE_IDLE) {
         return INVALID_OPERATION;
     }
@@ -136,6 +139,7 @@
 
 status_t NuPlayerDriver::setVideoSurfaceTexture(
         const sp<IGraphicBufferProducer> &bufferProducer) {
+    ALOGV("setVideoSurfaceTexture(%p)", this);
     Mutex::Autolock autoLock(mLock);
 
     if (mSetSurfaceInProgress) {
@@ -163,6 +167,7 @@
 }
 
 status_t NuPlayerDriver::prepare() {
+    ALOGV("prepare(%p)", this);
     Mutex::Autolock autoLock(mLock);
     return prepare_l();
 }
@@ -197,6 +202,7 @@
 }
 
 status_t NuPlayerDriver::prepareAsync() {
+    ALOGV("prepareAsync(%p)", this);
     Mutex::Autolock autoLock(mLock);
 
     switch (mState) {
@@ -218,6 +224,7 @@
 }
 
 status_t NuPlayerDriver::start() {
+    ALOGD("start(%p)", this);
     Mutex::Autolock autoLock(mLock);
 
     switch (mState) {
@@ -292,6 +299,7 @@
 }
 
 status_t NuPlayerDriver::stop() {
+    ALOGD("stop(%p)", this);
     Mutex::Autolock autoLock(mLock);
 
     switch (mState) {
@@ -346,6 +354,7 @@
 }
 
 status_t NuPlayerDriver::seekTo(int msec) {
+    ALOGD("seekTo(%p) %d ms", this, msec);
     Mutex::Autolock autoLock(mLock);
 
     int64_t seekTimeUs = msec * 1000ll;
@@ -430,6 +439,7 @@
 }
 
 status_t NuPlayerDriver::reset() {
+    ALOGD("reset(%p)", this);
     Mutex::Autolock autoLock(mLock);
 
     switch (mState) {
@@ -572,6 +582,7 @@
 }
 
 void NuPlayerDriver::notifyResetComplete() {
+    ALOGI("notifyResetComplete(%p)", this);
     Mutex::Autolock autoLock(mLock);
 
     CHECK_EQ(mState, STATE_RESET_IN_PROGRESS);
@@ -580,6 +591,7 @@
 }
 
 void NuPlayerDriver::notifySetSurfaceComplete() {
+    ALOGV("notifySetSurfaceComplete(%p)", this);
     Mutex::Autolock autoLock(mLock);
 
     CHECK(mSetSurfaceInProgress);
@@ -602,6 +614,7 @@
 }
 
 void NuPlayerDriver::notifySeekComplete() {
+    ALOGV("notifySeekComplete(%p)", this);
     Mutex::Autolock autoLock(mLock);
     notifySeekComplete_l();
 }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 958ab5b..d6bf1de 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1052,7 +1052,7 @@
             // become stale. Assuming that the MixerThread runs 20ms, with FastMixer at 5ms,
             // the max latency should be about 25ms with an average around 12ms (to be verified).
             // For safety we use 100ms.
-            ALOGW("getTimestamp: returned stale timestamp nowUs(%lld) numFramesPlayedAt(%lld)",
+            ALOGV("getTimestamp: returned stale timestamp nowUs(%lld) numFramesPlayedAt(%lld)",
                     (long long)nowUs, (long long)numFramesPlayedAt);
             numFramesPlayedAt = nowUs - kStaleTimestamp100ms;
         }
@@ -1082,7 +1082,7 @@
         //     numFramesPlayedAt, by a time amount greater than numFramesPlayed.
         //
         // Both of these are transitory conditions.
-        ALOGW("getPlayedOutAudioDurationUs: negative timestamp %lld set to zero", (long long)durationUs);
+        ALOGV("getPlayedOutAudioDurationUs: negative duration %lld set to zero", (long long)durationUs);
         durationUs = 0;
     }
     ALOGV("getPlayedOutAudioDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)",
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 4589ed1..b6ac2a0 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -32,6 +32,7 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
 
 #include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/MediaCodecList.h>
@@ -1359,6 +1360,7 @@
             int32_t isADTS, aacProfile;
             int32_t sbrMode;
             int32_t maxOutputChannelCount;
+            int32_t pcmLimiterEnable;
             drcParams_t drc;
             if (!msg->findInt32("is-adts", &isADTS)) {
                 isADTS = 0;
@@ -1373,6 +1375,10 @@
             if (!msg->findInt32("aac-max-output-channel_count", &maxOutputChannelCount)) {
                 maxOutputChannelCount = -1;
             }
+            if (!msg->findInt32("aac-pcm-limiter-enable", &pcmLimiterEnable)) {
+                // value is unknown
+                pcmLimiterEnable = -1;
+            }
             if (!msg->findInt32("aac-encoded-target-level", &drc.encodedTargetLevel)) {
                 // value is unknown
                 drc.encodedTargetLevel = -1;
@@ -1396,7 +1402,8 @@
 
             err = setupAACCodec(
                     encoder, numChannels, sampleRate, bitRate, aacProfile,
-                    isADTS != 0, sbrMode, maxOutputChannelCount, drc);
+                    isADTS != 0, sbrMode, maxOutputChannelCount, drc,
+                    pcmLimiterEnable);
         }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
         err = setupAMRCodec(encoder, false /* isWAMR */, bitRate);
@@ -1561,7 +1568,8 @@
 status_t ACodec::setupAACCodec(
         bool encoder, int32_t numChannels, int32_t sampleRate,
         int32_t bitRate, int32_t aacProfile, bool isADTS, int32_t sbrMode,
-        int32_t maxOutputChannelCount, const drcParams_t& drc) {
+        int32_t maxOutputChannelCount, const drcParams_t& drc,
+        int32_t pcmLimiterEnable) {
     if (encoder && isADTS) {
         return -EINVAL;
     }
@@ -1691,6 +1699,7 @@
     presentation.nHeavyCompression = drc.heavyCompression;
     presentation.nTargetReferenceLevel = drc.targetRefLevel;
     presentation.nEncodedTargetLevel = drc.encodedTargetLevel;
+    presentation.nPCMLimiterEnable = pcmLimiterEnable;
 
     status_t res = mOMX->setParameter(mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
     if (res == OK) {
@@ -2482,6 +2491,58 @@
     return setupErrorCorrectionParameters();
 }
 
+// static
+int /* OMX_VIDEO_AVCLEVELTYPE */ ACodec::getAVCLevelFor(
+        int width, int height, int rate, int bitrate,
+        OMX_VIDEO_AVCPROFILETYPE profile) {
+    // convert bitrate to main/baseline profile kbps equivalent
+    switch (profile) {
+        case OMX_VIDEO_AVCProfileHigh10:
+            bitrate = divUp(bitrate, 3000); break;
+        case OMX_VIDEO_AVCProfileHigh:
+            bitrate = divUp(bitrate, 1250); break;
+        default:
+            bitrate = divUp(bitrate, 1000); break;
+    }
+
+    // convert size and rate to MBs
+    width = divUp(width, 16);
+    height = divUp(height, 16);
+    int mbs = width * height;
+    rate *= mbs;
+    int maxDimension = max(width, height);
+
+    static const int limits[][5] = {
+        /*   MBps     MB   dim  bitrate        level */
+        {    1485,    99,  28,     64, OMX_VIDEO_AVCLevel1  },
+        {    1485,    99,  28,    128, OMX_VIDEO_AVCLevel1b },
+        {    3000,   396,  56,    192, OMX_VIDEO_AVCLevel11 },
+        {    6000,   396,  56,    384, OMX_VIDEO_AVCLevel12 },
+        {   11880,   396,  56,    768, OMX_VIDEO_AVCLevel13 },
+        {   11880,   396,  56,   2000, OMX_VIDEO_AVCLevel2  },
+        {   19800,   792,  79,   4000, OMX_VIDEO_AVCLevel21 },
+        {   20250,  1620, 113,   4000, OMX_VIDEO_AVCLevel22 },
+        {   40500,  1620, 113,  10000, OMX_VIDEO_AVCLevel3  },
+        {  108000,  3600, 169,  14000, OMX_VIDEO_AVCLevel31 },
+        {  216000,  5120, 202,  20000, OMX_VIDEO_AVCLevel32 },
+        {  245760,  8192, 256,  20000, OMX_VIDEO_AVCLevel4  },
+        {  245760,  8192, 256,  50000, OMX_VIDEO_AVCLevel41 },
+        {  522240,  8704, 263,  50000, OMX_VIDEO_AVCLevel42 },
+        {  589824, 22080, 420, 135000, OMX_VIDEO_AVCLevel5  },
+        {  983040, 36864, 543, 240000, OMX_VIDEO_AVCLevel51 },
+        { 2073600, 36864, 543, 240000, OMX_VIDEO_AVCLevel52 },
+    };
+
+    for (size_t i = 0; i < ARRAY_SIZE(limits); i++) {
+        const int (&limit)[5] = limits[i];
+        if (rate <= limit[0] && mbs <= limit[1] && maxDimension <= limit[2]
+                && bitrate <= limit[3]) {
+            return limit[4];
+        }
+    }
+    return 0;
+}
+
 status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) {
     int32_t bitrate, iFrameInterval;
     if (!msg->findInt32("bitrate", &bitrate)
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index a72cbd5..c99db84 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -186,7 +186,8 @@
         const sp<IMediaHTTPService> &httpService,
         const char *uri,
         const KeyedVector<String8, String8> *headers,
-        String8 *contentType) {
+        String8 *contentType,
+        HTTPBase *httpSource) {
     if (contentType != NULL) {
         *contentType = "";
     }
@@ -204,14 +205,15 @@
             return NULL;
         }
 
-        sp<IMediaHTTPConnection> conn = httpService->makeHTTPConnection();
-        if (conn == NULL) {
-            ALOGE("Failed to make http connection from http service!");
-            return NULL;
+        if (httpSource == NULL) {
+            sp<IMediaHTTPConnection> conn = httpService->makeHTTPConnection();
+            if (conn == NULL) {
+                ALOGE("Failed to make http connection from http service!");
+                return NULL;
+            }
+            httpSource = new MediaHTTP(conn);
         }
 
-        sp<HTTPBase> httpSource = new MediaHTTP(conn);
-
         String8 tmp;
         if (isWidevine) {
             tmp = String8("http://");
@@ -264,6 +266,19 @@
     return source;
 }
 
+sp<DataSource> DataSource::CreateMediaHTTP(const sp<IMediaHTTPService> &httpService) {
+    if (httpService == NULL) {
+        return NULL;
+    }
+
+    sp<IMediaHTTPConnection> conn = httpService->makeHTTPConnection();
+    if (conn == NULL) {
+        return NULL;
+    } else {
+        return new MediaHTTP(conn);
+    }
+}
+
 String8 DataSource::getMIMEType() const {
     return String8("application/octet-stream");
 }
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index b56819c..b568063 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -2141,11 +2141,24 @@
         return;
     }
 
-    if ((mFlags & (kFlagStickyError
+    bool isErrorOrOutputChanged =
+            (mFlags & (kFlagStickyError
                     | kFlagOutputBuffersChanged
-                    | kFlagOutputFormatChanged))
+                    | kFlagOutputFormatChanged));
+
+    if (isErrorOrOutputChanged
             || !mAvailPortBuffers[kPortIndexInput].empty()
             || !mAvailPortBuffers[kPortIndexOutput].empty()) {
+        mActivityNotify->setInt32("input-buffers",
+                mAvailPortBuffers[kPortIndexInput].size());
+
+        if (isErrorOrOutputChanged) {
+            // we want consumer to dequeue as many times as it can
+            mActivityNotify->setInt32("output-buffers", INT32_MAX);
+        } else {
+            mActivityNotify->setInt32("output-buffers",
+                    mAvailPortBuffers[kPortIndexOutput].size());
+        }
         mActivityNotify->post();
         mActivityNotify.clear();
     }
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index f469d4d..bd0a41d 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -456,6 +456,10 @@
     }
 
     Mutex::Autolock autoLock(mLock);
+    if (mDisconnecting) {
+        mCondition.signal();
+        return;
+    }
 
     CHECK(mAsyncResult == NULL);
 
@@ -502,6 +506,9 @@
     ALOGV("readAt offset %lld, size %zu", offset, size);
 
     Mutex::Autolock autoLock(mLock);
+    if (mDisconnecting) {
+        return ERROR_END_OF_STREAM;
+    }
 
     // If the request can be completely satisfied from the cache, do so.
 
@@ -528,6 +535,7 @@
     }
 
     if (mDisconnecting) {
+        mAsyncResult.clear();
         return ERROR_END_OF_STREAM;
     }
 
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index e701e9e..1b6eac4 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -368,6 +368,10 @@
                         aacPresParams->nEncodedTargetLevel);
                 updateDrcWrapper = true;
             }
+            if (aacPresParams->nPCMLimiterEnable >= 0) {
+                aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE,
+                        (aacPresParams->nPCMLimiterEnable != 0));
+            }
             if (updateDrcWrapper) {
                 mDrcWrap.update();
             }
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index d98fa80..1f4b6fd 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -156,7 +156,8 @@
                 (mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE;
 
             Bool success = PVInitVideoDecoder(
-                    mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode);
+                    mHandle, vol_data, &vol_size, 1,
+                    outputBufferWidth(), outputBufferHeight(), mode);
 
             if (!success) {
                 ALOGW("PVInitVideoDecoder failed. Unsupported content?");
@@ -321,7 +322,7 @@
 
             vol_data[0] = NULL;
             if (!PVInitVideoDecoder(
-                    mHandle, vol_data, &vol_size, 1, mWidth, mHeight,
+                    mHandle, vol_data, &vol_size, 1, outputBufferWidth(), outputBufferHeight(),
                     H263_MODE)) {
                 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
                 mSignalledError = true;
diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
index 3d20a79..2f83610 100644
--- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
@@ -166,8 +166,15 @@
     bool sizeChanged = (width != mWidth || height != mHeight);
     bool updateCrop = (cropSettingsMode == kCropUnSet);
     bool cropChanged = (cropSettingsMode == kCropChanged);
+    bool strideChanged = false;
+    if (fakeStride) {
+        OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef;
+        if (def->format.video.nStride != width || def->format.video.nSliceHeight != height) {
+            strideChanged = true;
+        }
+    }
 
-    if (sizeChanged || cropChanged) {
+    if (sizeChanged || cropChanged || strideChanged) {
         mWidth = width;
         mHeight = height;
 
diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk
index 903af49..99b480ad 100644
--- a/media/libstagefright/tests/Android.mk
+++ b/media/libstagefright/tests/Android.mk
@@ -9,7 +9,7 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_SRC_FILES := \
-    SurfaceMediaSource_test.cpp \
+	SurfaceMediaSource_test.cpp \
 	DummyRecorder.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
@@ -33,10 +33,10 @@
 	libgtest_main \
 
 LOCAL_C_INCLUDES := \
-    bionic \
-    bionic/libstdc++/include \
-    external/gtest/include \
-    external/stlport/stlport \
+	bionic \
+	bionic/libstdc++/include \
+	external/gtest/include \
+	external/stlport/stlport \
 	frameworks/av/media/libstagefright \
 	frameworks/av/media/libstagefright/include \
 	$(TOP)/frameworks/native/include/media/openmax \
@@ -47,6 +47,41 @@
 
 endif
 
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := Utils_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+	Utils_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	liblog \
+	libmedia \
+	libstagefright \
+	libstagefright_foundation \
+	libstagefright_omx \
+	libstlport \
+
+LOCAL_STATIC_LIBRARIES := \
+	libgtest \
+	libgtest_main \
+
+LOCAL_C_INCLUDES := \
+	bionic \
+	bionic/libstdc++/include \
+	external/gtest/include \
+	external/stlport/stlport \
+	frameworks/av/include \
+	frameworks/av/media/libstagefright \
+	frameworks/av/media/libstagefright/include \
+	$(TOP)/frameworks/native/include/media/openmax \
+
+include $(BUILD_EXECUTABLE)
+
 # Include subdirectory makefiles
 # ============================================================
 
diff --git a/media/libstagefright/tests/Utils_test.cpp b/media/libstagefright/tests/Utils_test.cpp
new file mode 100644
index 0000000..f2825dd
--- /dev/null
+++ b/media/libstagefright/tests/Utils_test.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Utils_test"
+
+#include <gtest/gtest.h>
+#include <utils/String8.h>
+#include <utils/Errors.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+class UtilsTest : public ::testing::Test {
+};
+
+TEST_F(UtilsTest, TestFourCC) {
+    ASSERT_EQ(FOURCC('s', 't', 'm' , 'u'), 'stmu');
+}
+
+TEST_F(UtilsTest, TestMathTemplates) {
+    ASSERT_EQ(divRound(-10, -4), 3);
+    ASSERT_EQ(divRound(-11, -4), 3);
+    ASSERT_EQ(divRound(-12, -4), 3);
+    ASSERT_EQ(divRound(-13, -4), 3);
+    ASSERT_EQ(divRound(-14, -4), 4);
+
+    ASSERT_EQ(divRound(10, -4), -3);
+    ASSERT_EQ(divRound(11, -4), -3);
+    ASSERT_EQ(divRound(12, -4), -3);
+    ASSERT_EQ(divRound(13, -4), -3);
+    ASSERT_EQ(divRound(14, -4), -4);
+
+    ASSERT_EQ(divRound(-10, 4), -3);
+    ASSERT_EQ(divRound(-11, 4), -3);
+    ASSERT_EQ(divRound(-12, 4), -3);
+    ASSERT_EQ(divRound(-13, 4), -3);
+    ASSERT_EQ(divRound(-14, 4), -4);
+
+    ASSERT_EQ(divRound(10, 4), 3);
+    ASSERT_EQ(divRound(11, 4), 3);
+    ASSERT_EQ(divRound(12, 4), 3);
+    ASSERT_EQ(divRound(13, 4), 3);
+    ASSERT_EQ(divRound(14, 4), 4);
+
+    ASSERT_EQ(divUp(-11, -4), 3);
+    ASSERT_EQ(divUp(-12, -4), 3);
+    ASSERT_EQ(divUp(-13, -4), 4);
+
+    ASSERT_EQ(divUp(11, -4), -2);
+    ASSERT_EQ(divUp(12, -4), -3);
+    ASSERT_EQ(divUp(13, -4), -3);
+
+    ASSERT_EQ(divUp(-11, 4), -2);
+    ASSERT_EQ(divUp(-12, 4), -3);
+    ASSERT_EQ(divUp(-13, 4), -3);
+
+    ASSERT_EQ(divUp(11, 4), 3);
+    ASSERT_EQ(divUp(12, 4), 3);
+    ASSERT_EQ(divUp(13, 4), 4);
+
+    ASSERT_EQ(abs(5L), 5L);
+    ASSERT_EQ(abs(-25), 25);
+
+    ASSERT_EQ(min(5.6f, 6.0f), 5.6f);
+    ASSERT_EQ(min(6.0f, 5.6f), 5.6f);
+    ASSERT_EQ(min(-4.3, 8.6), -4.3);
+    ASSERT_EQ(min(8.6, -4.3), -4.3);
+
+    ASSERT_EQ(max(5.6f, 6.0f), 6.0f);
+    ASSERT_EQ(max(6.0f, 5.6f), 6.0f);
+    ASSERT_EQ(max(-4.3, 8.6), 8.6);
+    ASSERT_EQ(max(8.6, -4.3), 8.6);
+
+    ASSERT_EQ(periodicError(124, 100), 24);
+    ASSERT_EQ(periodicError(288, 100), 12);
+    ASSERT_EQ(periodicError(-345, 100), 45);
+    ASSERT_EQ(periodicError(-493, 100), 7);
+    ASSERT_EQ(periodicError(-550, 100), 50);
+    ASSERT_EQ(periodicError(-600, 100), 0);
+}
+
+} // namespace android
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index e200857..e48af20 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1436,6 +1436,16 @@
                                                   IPCThreadState::self()->getCallingUid(),
                                                   flags, tid, &lStatus);
         LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0));
+
+        if (lStatus == NO_ERROR) {
+            // Check if one effect chain was awaiting for an AudioRecord to be created on this
+            // session and move it to this thread.
+            sp<EffectChain> chain = getOrphanEffectChain_l((audio_session_t)lSessionId);
+            if (chain != 0) {
+                Mutex::Autolock _l(thread->mLock);
+                thread->addEffectChain_l(chain);
+            }
+        }
     }
 
     if (lStatus != NO_ERROR) {
@@ -2034,14 +2044,41 @@
         }
 
         ALOGV("closeInput() %d", input);
+
+        // If we still have effect chains, it means that a client still holds a handle
+        // on at least one effect. We must either move the chain to an existing thread with the
+        // same session ID or put it aside in case a new record thread is opened for a
+        // new capture on the same session
+        sp<EffectChain> chain;
         {
-            // If we still have effect chains, it means that a client still holds a handle
-            // on at least one effect. We must keep the chain alive in case a new record
-            // thread is opened for a new capture on the same session
             Mutex::Autolock _sl(thread->mLock);
             Vector< sp<EffectChain> > effectChains = thread->getEffectChains_l();
-            for (size_t i = 0; i < effectChains.size(); i++) {
-                putOrphanEffectChain_l(effectChains[i]);
+            // Note: maximum one chain per record thread
+            if (effectChains.size() != 0) {
+                chain = effectChains[0];
+            }
+        }
+        if (chain != 0) {
+            // first check if a record thread is already opened with a client on the same session.
+            // This should only happen in case of overlap between one thread tear down and the
+            // creation of its replacement
+            size_t i;
+            for (i = 0; i < mRecordThreads.size(); i++) {
+                sp<RecordThread> t = mRecordThreads.valueAt(i);
+                if (t == thread) {
+                    continue;
+                }
+                if (t->hasAudioSession(chain->sessionId()) != 0) {
+                    Mutex::Autolock _l(t->mLock);
+                    ALOGV("closeInput() found thread %d for effect session %d",
+                          t->id(), chain->sessionId());
+                    t->addEffectChain_l(chain);
+                    break;
+                }
+            }
+            // put the chain aside if we could not find a record thread with the same session id.
+            if (i == mRecordThreads.size()) {
+                putOrphanEffectChain_l(chain);
             }
         }
         audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL);
@@ -2478,6 +2515,7 @@
             // session and used it instead of creating a new one.
             sp<EffectChain> chain = getOrphanEffectChain_l((audio_session_t)sessionId);
             if (chain != 0) {
+                Mutex::Autolock _l(thread->mLock);
                 thread->addEffectChain_l(chain);
             }
         }
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 15f1f23..bcaf8ae 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -440,6 +440,20 @@
     return status;
 }
 
+void AudioFlinger::EffectModule::addEffectToHal_l()
+{
+    if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
+         (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
+        sp<ThreadBase> thread = mThread.promote();
+        if (thread != 0) {
+            audio_stream_t *stream = thread->stream();
+            if (stream != NULL) {
+                stream->add_audio_effect(stream, mEffectInterface);
+            }
+        }
+    }
+}
+
 status_t AudioFlinger::EffectModule::start()
 {
     Mutex::Autolock _l(mLock);
@@ -465,15 +479,11 @@
     if (status == 0) {
         status = cmdStatus;
     }
-    if (status == 0 &&
-            ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
-             (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC)) {
-        sp<ThreadBase> thread = mThread.promote();
-        if (thread != 0) {
-            audio_stream_t *stream = thread->stream();
-            if (stream != NULL) {
-                stream->add_audio_effect(stream, mEffectInterface);
-            }
+    if (status == 0) {
+        addEffectToHal_l();
+        sp<EffectChain> chain = mChain.promote();
+        if (chain != 0) {
+            chain->forceVolume();
         }
     }
     return status;
@@ -1326,7 +1336,7 @@
                                         int sessionId)
     : mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
       mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
-      mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
+      mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX), mForceVolume(false)
 {
     mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
     if (thread == NULL) {
@@ -1649,7 +1659,8 @@
         }
     }
 
-    if (ctrlIdx == mVolumeCtrlIdx && *left == mLeftVolume && *right == mRightVolume) {
+    if (!isVolumeForced() && ctrlIdx == mVolumeCtrlIdx &&
+            *left == mLeftVolume && *right == mRightVolume) {
         if (hasControl) {
             *left = mNewLeftVolume;
             *right = mNewRightVolume;
@@ -1690,6 +1701,17 @@
     return hasControl;
 }
 
+void AudioFlinger::EffectChain::syncHalEffectsState()
+{
+    Mutex::Autolock _l(mLock);
+    for (size_t i = 0; i < mEffects.size(); i++) {
+        if (mEffects[i]->state() == EffectModule::ACTIVE ||
+                mEffects[i]->state() == EffectModule::STOPPING) {
+            mEffects[i]->addEffectToHal_l();
+        }
+    }
+}
+
 void AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args)
 {
     const size_t SIZE = 256;
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index eaf90e7..6f93f81 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -119,6 +119,7 @@
                         { return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; }
     status_t         setOffloaded(bool offloaded, audio_io_handle_t io);
     bool             isOffloaded() const;
+    void             addEffectToHal_l();
 
     void             dump(int fd, const Vector<String16>& args);
 
@@ -318,6 +319,14 @@
     // At least one non offloadable effect in the chain is enabled
     bool isNonOffloadableEnabled();
 
+    // use release_cas because we don't care about the observed value, just want to make sure the
+    // new value is observable.
+    void forceVolume() { android_atomic_release_cas(false, true, &mForceVolume); }
+    // use acquire_cas because we are interested in the observed value and
+    // we are the only observers.
+    bool isVolumeForced() { return (android_atomic_acquire_cas(true, false, &mForceVolume) == 0); }
+
+    void syncHalEffectsState();
 
     void dump(int fd, const Vector<String16>& args);
 
@@ -375,4 +384,5 @@
     // timeLow fields among effect type UUIDs.
     // Updated by updateSuspendedSessions_l() only.
     KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
+    volatile int32_t mForceVolume; // force next volume command because a new effect was enabled
 };
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 66edf45..44e34b7 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2101,6 +2101,7 @@
 
     // If an NBAIO sink is present, use it to write the normal mixer's submix
     if (mNormalSink != 0) {
+
         const size_t count = mBytesRemaining / mFrameSize;
 
         ATRACE_BEGIN("write");
@@ -2126,7 +2127,7 @@
             size_t totalFramesWritten = mNormalSink->framesWritten();
             if (totalFramesWritten >= mLatchD.mTimestamp.mPosition) {
                 mLatchD.mUnpresentedFrames = totalFramesWritten - mLatchD.mTimestamp.mPosition;
-                // mLatchD.mFramesReleased is set in threadloop_mix()
+                // mLatchD.mFramesReleased is set immediately before D is clocked into Q
                 mLatchDValid = true;
             }
         }
@@ -2419,6 +2420,18 @@
                 logString = NULL;
             }
 
+            // Gather the framesReleased counters for all active tracks,
+            // and latch them atomically with the timestamp.
+            // FIXME We're using raw pointers as indices. A unique track ID would be a better index.
+            mLatchD.mFramesReleased.clear();
+            size_t size = mActiveTracks.size();
+            for (size_t i = 0; i < size; i++) {
+                sp<Track> t = mActiveTracks[i].promote();
+                if (t != 0) {
+                    mLatchD.mFramesReleased.add(t.get(),
+                            t->mAudioTrackServerProxy->framesReleased());
+                }
+            }
             if (mLatchDValid) {
                 mLatchQ = mLatchD;
                 mLatchDValid = false;
@@ -3095,17 +3108,6 @@
     standbyTime = systemTime() + standbyDelay;
     //TODO: delay standby when effects have a tail
 
-    mLatchD.mFramesReleased.clear();
-    {
-        Mutex::Autolock _l(mLock);
-        size_t size = mActiveTracks.size();
-        for (size_t i = 0; i < size; i++) {
-            sp<Track> t = mActiveTracks[i].promote();
-            if (t != 0) {
-                mLatchD.mFramesReleased.add(t.get(), t->mAudioTrackServerProxy->framesReleased());
-            }
-        }
-    }
 }
 
 void AudioFlinger::MixerThread::threadLoop_sleepTime()
@@ -6205,6 +6207,10 @@
 
     checkSuspendOnAddEffectChain_l(chain);
 
+    // make sure enabled pre processing effects state is communicated to the HAL as we
+    // just moved them to a new input stream.
+    chain->syncHalEffectsState();
+
     mEffectChains.add(chain);
 
     return NO_ERROR;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 7d3b854..bb9aa18 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -821,7 +821,9 @@
         uint32_t        mUnpresentedFrames;
         KeyedVector<Track *, uint32_t> mFramesReleased;
     } mLatchD, mLatchQ;
-    bool mLatchDValid;  // true means mLatchD is valid, and clock it into latch at next opportunity
+    bool mLatchDValid;  // true means mLatchD is valid
+                        //     (except for mFramesReleased which is filled in later),
+                        //     and clock it into latch at next opportunity
     bool mLatchQValid;  // true means mLatchQ is valid
 };
 
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index b2d53cf..b9308fa 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -906,7 +906,8 @@
         //       be changed to something that is unique.  Or use a completely different strategy.
         ssize_t i = playbackThread->mLatchQ.mFramesReleased.indexOfKey(this);
         uint32_t framesWritten = i >= 0 ?
-                playbackThread->mLatchQ.mFramesReleased[i] : mAudioTrackServerProxy->framesReleased();
+                playbackThread->mLatchQ.mFramesReleased[i] :
+                mAudioTrackServerProxy->framesReleased();
         bool checkPreviousTimestamp = mPreviousValid && framesWritten >= mPreviousFramesWritten;
         if (framesWritten < unpresentedFrames) {
             mPreviousValid = false;