Merge "Revert "Fix uncertainty of one normal mix buffer in AudioTrack::getTimestamp"" into lmp-dev
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index da4c20c..df0dc58 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -267,7 +267,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/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index f84decd..016a764 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;
@@ -284,10 +286,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 +375,7 @@
         mSniffedMIME = "";
         mDataSource.clear();
         mCachedSource.clear();
+        mHttpSource.clear();
 
         cancelPollBuffering();
     }
@@ -479,6 +495,8 @@
         if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
             static_cast<NuCachedSource2 *>(mDataSource.get())->disconnect();
         }
+    } else if (mHttpSource != NULL) {
+        static_cast<HTTPBase *>(mHttpSource.get())->disconnect();
     }
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 24bb6af..5ed4d52 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -126,6 +126,7 @@
 
     sp<DataSource> mDataSource;
     sp<NuCachedSource2> mCachedSource;
+    sp<DataSource> mHttpSource;
     sp<WVMExtractor> mWVMExtractor;
     sp<MetaData> mFileMeta;
     DrmManagerClient *mDrmManagerClient;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 73ac057..6d10651 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -20,6 +20,8 @@
 
 #include "NuPlayerRenderer.h"
 
+#include <cutils/properties.h>
+
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -39,6 +41,16 @@
 // static
 const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll;
 
+static bool sFrameAccurateAVsync = false;
+
+static void readProperties() {
+    char value[PROPERTY_VALUE_MAX];
+    if (property_get("persist.sys.media.avsync", value, NULL)) {
+        sFrameAccurateAVsync =
+            !strcmp("1", value) || !strcasecmp("true", value);
+    }
+}
+
 NuPlayer::Renderer::Renderer(
         const sp<MediaPlayerBase::AudioSink> &sink,
         const sp<AMessage> &notify,
@@ -68,6 +80,7 @@
       mVideoLateByUs(0ll),
       mAudioOffloadPauseTimeoutGeneration(0),
       mAudioOffloadTornDown(false) {
+    readProperties();
 }
 
 NuPlayer::Renderer::~Renderer() {
@@ -576,6 +589,11 @@
 
     ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs);
     // post 2 display refreshes before rendering is due
+    // FIXME currently this increases power consumption, so unless frame-accurate
+    // AV sync is requested, post closer to required render time (at 0.63 vsyncs)
+    if (!sFrameAccurateAVsync) {
+        twoVsyncsUs >>= 4;
+    }
     msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0);
 
     mDrainVideoQueuePending = true;
@@ -976,6 +994,8 @@
 }
 
 void NuPlayer::Renderer::onResume() {
+    readProperties();
+
     if (!mPaused) {
         return;
     }
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 4589ed1..d6fba98 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1359,6 +1359,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 +1374,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 +1401,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 +1567,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 +1698,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) {
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/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/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 7a9a1a3..fba6b09 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -494,6 +494,10 @@
                     AString uri;
                     CHECK(msg->findString("uri", &uri));
 
+                    if (mFetcherInfos.indexOfKey(uri) < 0) {
+                        ALOGE("couldn't find uri");
+                        break;
+                    }
                     FetcherInfo *info = &mFetcherInfos.editValueFor(uri);
                     info->mIsPrepared = true;
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 818bb05..12e09ab 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2803,6 +2803,7 @@
 
         // create a MonoPipe to connect our submix to FastMixer
         NBAIO_Format format = mOutputSink->format();
+        NBAIO_Format origformat = format;
         // adjust format to match that of the Fast Mixer
         format.mFormat = fastMixerFormat;
         format.mFrameSize = audio_bytes_per_sample(format.mFormat) * format.mChannelCount;
@@ -2822,14 +2823,15 @@
 #ifdef TEE_SINK
         if (mTeeSinkOutputEnabled) {
             // create a Pipe to archive a copy of FastMixer's output for dumpsys
-            Pipe *teeSink = new Pipe(mTeeSinkOutputFrames, format);
+            Pipe *teeSink = new Pipe(mTeeSinkOutputFrames, origformat);
+            const NBAIO_Format offers2[1] = {origformat};
             numCounterOffers = 0;
-            index = teeSink->negotiate(offers, 1, NULL, numCounterOffers);
+            index = teeSink->negotiate(offers2, 1, NULL, numCounterOffers);
             ALOG_ASSERT(index == 0);
             mTeeSink = teeSink;
             PipeReader *teeSource = new PipeReader(*teeSink);
             numCounterOffers = 0;
-            index = teeSource->negotiate(offers, 1, NULL, numCounterOffers);
+            index = teeSource->negotiate(offers2, 1, NULL, numCounterOffers);
             ALOG_ASSERT(index == 0);
             mTeeSource = teeSource;
         }
@@ -4040,6 +4042,9 @@
                         track->mState = TrackBase::STOPPED;
                     }
                     if (track->isStopped()) {
+                        if (track->mState == TrackBase::FLUSHED) {
+                            flushHw_l();
+                        }
                         track->reset();
                     }
                     tracksToRemove->add(track);
@@ -4212,6 +4217,12 @@
     }
 }
 
+void AudioFlinger::DirectOutputThread::flushHw_l()
+{
+    if (mOutput->stream->flush != NULL)
+        mOutput->stream->flush(mOutput->stream);
+}
+
 // ----------------------------------------------------------------------------
 
 AudioFlinger::AsyncCallbackThread::AsyncCallbackThread(
@@ -4579,7 +4590,7 @@
 
 void AudioFlinger::OffloadThread::flushHw_l()
 {
-    mOutput->stream->flush(mOutput->stream);
+    DirectOutputThread::flushHw_l();
     // Flush anything still waiting in the mixbuffer
     mCurrentWriteLength = 0;
     mBytesRemaining = 0;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index fd025b5..7af5264 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -894,6 +894,7 @@
 
     virtual     bool        checkForNewParameter_l(const String8& keyValuePair,
                                                    status_t& status);
+    virtual     void        flushHw_l();
 
 protected:
     virtual     int         getTrackName_l(audio_channel_mask_t channelMask,
@@ -929,6 +930,7 @@
     OffloadThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
                         audio_io_handle_t id, uint32_t device);
     virtual                 ~OffloadThread() {};
+    virtual     void        flushHw_l();
 
 protected:
     // threadLoop snippets
@@ -941,9 +943,6 @@
     virtual     void        onAddNewTrack_l();
 
 private:
-                void        flushHw_l();
-
-private:
     bool        mHwPaused;
     bool        mFlushPending;
     size_t      mPausedWriteLength;     // length in bytes of write interrupted by pause
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index c0a75b9..75190f3 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -824,6 +824,10 @@
             // remove from active track list, reset(), and trigger presentation complete
             if (playbackThread->mActiveTracks.indexOf(this) < 0) {
                 reset();
+                if (thread->type() == ThreadBase::DIRECT) {
+                    DirectOutputThread *t = (DirectOutputThread *)playbackThread;
+                    t->flushHw_l();
+                }
             }
         }
         // Prevent flush being lost if the track is flushed and then resumed
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index d0b990f..536987a 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -930,7 +930,9 @@
     sp<IOProfile> profile;
 
     // skip direct output selection if the request can obviously be attached to a mixed output
-    if (audio_is_linear_pcm(format) && samplingRate <= MAX_MIXER_SAMPLING_RATE &&
+    // and not explicitly requested
+    if (((flags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) &&
+            audio_is_linear_pcm(format) && samplingRate <= MAX_MIXER_SAMPLING_RATE &&
             audio_channel_count_from_out_mask(channelMask) <= 2) {
         goto non_direct_output;
     }
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 48ec730..3610362 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -1049,32 +1049,35 @@
         }
     }
 
-    if (mZslProcessor->getStreamId() != NO_STREAM) {
-        ALOGV("%s: Camera %d: Clearing out zsl stream before "
-                "creating recording stream", __FUNCTION__, mCameraId);
-        res = mStreamingProcessor->stopStream();
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Can't stop streaming to delete callback stream",
-                    __FUNCTION__, mCameraId);
-            return res;
-        }
-        res = mDevice->waitUntilDrained();
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)",
-                    __FUNCTION__, mCameraId, strerror(-res), res);
-        }
-        res = mZslProcessor->clearZslQueue();
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Can't clear zsl queue",
-                    __FUNCTION__, mCameraId);
-            return res;
-        }
-        res = mZslProcessor->deleteStream();
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Unable to delete zsl stream before "
-                    "record: %s (%d)", __FUNCTION__, mCameraId,
-                    strerror(-res), res);
-            return res;
+    // On current HALs, clean up ZSL before transitioning into recording
+    if (mDeviceVersion != CAMERA_DEVICE_API_VERSION_2_0) {
+        if (mZslProcessor->getStreamId() != NO_STREAM) {
+            ALOGV("%s: Camera %d: Clearing out zsl stream before "
+                    "creating recording stream", __FUNCTION__, mCameraId);
+            res = mStreamingProcessor->stopStream();
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Can't stop streaming to delete callback stream",
+                        __FUNCTION__, mCameraId);
+                return res;
+            }
+            res = mDevice->waitUntilDrained();
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)",
+                        __FUNCTION__, mCameraId, strerror(-res), res);
+            }
+            res = mZslProcessor->clearZslQueue();
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Can't clear zsl queue",
+                        __FUNCTION__, mCameraId);
+                return res;
+            }
+            res = mZslProcessor->deleteStream();
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Unable to delete zsl stream before "
+                        "record: %s (%d)", __FUNCTION__, mCameraId,
+                        strerror(-res), res);
+                return res;
+            }
         }
     }
 
@@ -1082,34 +1085,53 @@
     // and we can't fail record start without stagefright asserting.
     params.previewCallbackFlags = 0;
 
-    bool recordingStreamNeedsUpdate;
-    res = mStreamingProcessor->recordingStreamNeedsUpdate(params, &recordingStreamNeedsUpdate);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Can't query recording stream",
-                __FUNCTION__, mCameraId);
-        return res;
-    }
-
-    if (recordingStreamNeedsUpdate) {
-        // Need to stop stream here so updateProcessorStream won't trigger configureStream
-        // Right now camera device cannot handle configureStream failure gracefully
-        // when device is streaming
-        res = mStreamingProcessor->stopStream();
+    if (mDeviceVersion != CAMERA_DEVICE_API_VERSION_2_0) {
+        // For newer devices, may need to reconfigure video snapshot JPEG sizes
+        // during recording startup, so need a more complex sequence here to
+        // ensure an early stream reconfiguration doesn't happen
+        bool recordingStreamNeedsUpdate;
+        res = mStreamingProcessor->recordingStreamNeedsUpdate(params, &recordingStreamNeedsUpdate);
         if (res != OK) {
-            ALOGE("%s: Camera %d: Can't stop streaming to update record stream",
+            ALOGE("%s: Camera %d: Can't query recording stream",
                     __FUNCTION__, mCameraId);
             return res;
         }
-        res = mDevice->waitUntilDrained();
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)",
-                    __FUNCTION__, mCameraId, strerror(-res), res);
+
+        if (recordingStreamNeedsUpdate) {
+            // Need to stop stream here so updateProcessorStream won't trigger configureStream
+            // Right now camera device cannot handle configureStream failure gracefully
+            // when device is streaming
+            res = mStreamingProcessor->stopStream();
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Can't stop streaming to update record "
+                        "stream", __FUNCTION__, mCameraId);
+                return res;
+            }
+            res = mDevice->waitUntilDrained();
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Waiting to stop streaming failed: "
+                        "%s (%d)", __FUNCTION__, mCameraId,
+                        strerror(-res), res);
+            }
+
+            res = updateProcessorStream<
+                    StreamingProcessor,
+                    &StreamingProcessor::updateRecordingStream>(
+                        mStreamingProcessor,
+                        params);
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Unable to update recording stream: "
+                        "%s (%d)", __FUNCTION__, mCameraId,
+                        strerror(-res), res);
+                return res;
+            }
         }
+    } else {
+        // Maintain call sequencing for HALv2 devices.
         res = updateProcessorStream<
                 StreamingProcessor,
                 &StreamingProcessor::updateRecordingStream>(mStreamingProcessor,
-                                                            params);
-
+                    params);
         if (res != OK) {
             ALOGE("%s: Camera %d: Unable to update recording stream: %s (%d)",
                     __FUNCTION__, mCameraId, strerror(-res), res);
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index cda98be..b433781 100644
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -89,14 +89,27 @@
         mCaptureConsumer->setFrameAvailableListener(this);
         mCaptureConsumer->setName(String8("Camera2Client::CaptureConsumer"));
         mCaptureWindow = new Surface(producer);
+    }
+
+    // Since ashmem heaps are rounded up to page size, don't reallocate if
+    // the capture heap isn't exactly the same size as the required JPEG buffer
+    const size_t HEAP_SLACK_FACTOR = 2;
+    if (mCaptureHeap == 0 ||
+            (mCaptureHeap->getSize() < static_cast<size_t>(maxJpegSize)) ||
+            (mCaptureHeap->getSize() >
+                    static_cast<size_t>(maxJpegSize) * HEAP_SLACK_FACTOR) ) {
         // Create memory for API consumption
-        mCaptureHeap = new MemoryHeapBase(maxJpegSize, 0, "Camera2Client::CaptureHeap");
+        mCaptureHeap.clear();
+        mCaptureHeap =
+                new MemoryHeapBase(maxJpegSize, 0, "Camera2Client::CaptureHeap");
         if (mCaptureHeap->getSize() == 0) {
             ALOGE("%s: Camera %d: Unable to allocate memory for capture",
                     __FUNCTION__, mId);
             return NO_MEMORY;
         }
     }
+    ALOGV("%s: Camera %d: JPEG capture heap now %d bytes; requested %d bytes",
+            __FUNCTION__, mId, mCaptureHeap->getSize(), maxJpegSize);
 
     if (mCaptureStreamId != NO_STREAM) {
         // Check if stream parameters have to change
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index bb72206..8f78103 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -154,7 +154,7 @@
                     mId, strerror(-res), res);
             return res;
         }
-        if (mDeleted || currentWidth != (uint32_t)params.fastInfo.arrayWidth ||
+        if (currentWidth != (uint32_t)params.fastInfo.arrayWidth ||
                 currentHeight != (uint32_t)params.fastInfo.arrayHeight) {
             res = device->deleteReprocessStream(mZslReprocessStreamId);
             if (res != OK) {