Merge "Fix race condition in AwesomePlayer"
diff --git a/cmds/screenrecord/Android.mk b/cmds/screenrecord/Android.mk
index d77fdb6..6747e60 100644
--- a/cmds/screenrecord/Android.mk
+++ b/cmds/screenrecord/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_SRC_FILES := \
 	screenrecord.cpp \
 	EglWindow.cpp \
+	FrameOutput.cpp \
 	TextRenderer.cpp \
 	Overlay.cpp \
 	Program.cpp
diff --git a/cmds/screenrecord/EglWindow.cpp b/cmds/screenrecord/EglWindow.cpp
index aa0517f..c16f2ad 100644
--- a/cmds/screenrecord/EglWindow.cpp
+++ b/cmds/screenrecord/EglWindow.cpp
@@ -35,11 +35,16 @@
 
 
 status_t EglWindow::createWindow(const sp<IGraphicBufferProducer>& surface) {
-    status_t err = eglSetupContext();
+    if (mEglSurface != EGL_NO_SURFACE) {
+        ALOGE("surface already created");
+        return UNKNOWN_ERROR;
+    }
+    status_t err = eglSetupContext(false);
     if (err != NO_ERROR) {
         return err;
     }
 
+    // Cache the current dimensions.  We're not expecting these to change.
     surface->query(NATIVE_WINDOW_WIDTH, &mWidth);
     surface->query(NATIVE_WINDOW_HEIGHT, &mHeight);
 
@@ -56,6 +61,34 @@
     return NO_ERROR;
 }
 
+status_t EglWindow::createPbuffer(int width, int height) {
+    if (mEglSurface != EGL_NO_SURFACE) {
+        ALOGE("surface already created");
+        return UNKNOWN_ERROR;
+    }
+    status_t err = eglSetupContext(true);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    mWidth = width;
+    mHeight = height;
+
+    EGLint pbufferAttribs[] = {
+            EGL_WIDTH, width,
+            EGL_HEIGHT, height,
+            EGL_NONE
+    };
+    mEglSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, pbufferAttribs);
+    if (mEglSurface == EGL_NO_SURFACE) {
+        ALOGE("eglCreatePbufferSurface error: %#x", eglGetError());
+        eglRelease();
+        return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
 status_t EglWindow::makeCurrent() const {
     if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
         ALOGE("eglMakeCurrent failed: %#x", eglGetError());
@@ -64,7 +97,7 @@
     return NO_ERROR;
 }
 
-status_t EglWindow::eglSetupContext() {
+status_t EglWindow::eglSetupContext(bool forPbuffer) {
     EGLBoolean result;
 
     mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@@ -82,17 +115,28 @@
     ALOGV("Initialized EGL v%d.%d", majorVersion, minorVersion);
 
     EGLint numConfigs = 0;
-    EGLint configAttribs[] = {
-        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
-        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-        EGL_RECORDABLE_ANDROID, 1,
-        EGL_RED_SIZE, 8,
-        EGL_GREEN_SIZE, 8,
-        EGL_BLUE_SIZE, 8,
-        EGL_NONE
+    EGLint windowConfigAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_RECORDABLE_ANDROID, 1,
+            EGL_RED_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE, 8,
+            // no alpha
+            EGL_NONE
     };
-    result = eglChooseConfig(mEglDisplay, configAttribs, &mEglConfig, 1,
-            &numConfigs);
+    EGLint pbufferConfigAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_RED_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE, 8,
+            EGL_ALPHA_SIZE, 8,
+            EGL_NONE
+    };
+    result = eglChooseConfig(mEglDisplay,
+            forPbuffer ? pbufferConfigAttribs : windowConfigAttribs,
+            &mEglConfig, 1, &numConfigs);
     if (result != EGL_TRUE) {
         ALOGE("eglChooseConfig error: %#x", eglGetError());
         return UNKNOWN_ERROR;
diff --git a/cmds/screenrecord/EglWindow.h b/cmds/screenrecord/EglWindow.h
index 02a2efc..69d0c31 100644
--- a/cmds/screenrecord/EglWindow.h
+++ b/cmds/screenrecord/EglWindow.h
@@ -44,6 +44,9 @@
     // Creates an EGL window for the supplied surface.
     status_t createWindow(const sp<IGraphicBufferProducer>& surface);
 
+    // Creates an EGL pbuffer surface.
+    status_t createPbuffer(int width, int height);
+
     // Return width and height values (obtained from IGBP).
     int getWidth() const { return mWidth; }
     int getHeight() const { return mHeight; }
@@ -65,7 +68,7 @@
     EglWindow& operator=(const EglWindow&);
 
     // Init display, create config and context.
-    status_t eglSetupContext();
+    status_t eglSetupContext(bool forPbuffer);
     void eglRelease();
 
     // Basic EGL goodies.
diff --git a/cmds/screenrecord/FrameOutput.cpp b/cmds/screenrecord/FrameOutput.cpp
new file mode 100644
index 0000000..b5cf2f9
--- /dev/null
+++ b/cmds/screenrecord/FrameOutput.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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_TAG "ScreenRecord"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include "FrameOutput.h"
+
+using namespace android;
+
+static const bool kShowTiming = false;      // set to "true" for debugging
+static const int kGlBytesPerPixel = 4;      // GL_RGBA
+static const int kOutBytesPerPixel = 3;     // RGB only
+
+inline void FrameOutput::setValueLE(uint8_t* buf, uint32_t value) {
+    // Since we're running on an Android device, we're (almost) guaranteed
+    // to be little-endian, and (almost) guaranteed that unaligned 32-bit
+    // writes will work without any performance penalty... but do it
+    // byte-by-byte anyway.
+    buf[0] = (uint8_t) value;
+    buf[1] = (uint8_t) (value >> 8);
+    buf[2] = (uint8_t) (value >> 16);
+    buf[3] = (uint8_t) (value >> 24);
+}
+
+status_t FrameOutput::createInputSurface(int width, int height,
+        sp<IGraphicBufferProducer>* pBufferProducer) {
+    status_t err;
+
+    err = mEglWindow.createPbuffer(width, height);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    mEglWindow.makeCurrent();
+
+    glViewport(0, 0, width, height);
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_CULL_FACE);
+
+    // Shader for rendering the external texture.
+    err = mExtTexProgram.setup(Program::PROGRAM_EXTERNAL_TEXTURE);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    // Input side (buffers from virtual display).
+    glGenTextures(1, &mExtTextureName);
+    if (mExtTextureName == 0) {
+        ALOGE("glGenTextures failed: %#x", glGetError());
+        return UNKNOWN_ERROR;
+    }
+
+    mBufferQueue = new BufferQueue(/*new GraphicBufferAlloc()*/);
+    mGlConsumer = new GLConsumer(mBufferQueue, mExtTextureName,
+                GL_TEXTURE_EXTERNAL_OES);
+    mGlConsumer->setName(String8("virtual display"));
+    mGlConsumer->setDefaultBufferSize(width, height);
+    mGlConsumer->setDefaultMaxBufferCount(5);
+    mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
+
+    mGlConsumer->setFrameAvailableListener(this);
+
+    mPixelBuf = new uint8_t[width * height * kGlBytesPerPixel];
+
+    *pBufferProducer = mBufferQueue;
+
+    ALOGD("FrameOutput::createInputSurface OK");
+    return NO_ERROR;
+}
+
+status_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec) {
+    Mutex::Autolock _l(mMutex);
+    ALOGV("copyFrame %ld\n", timeoutUsec);
+
+    if (!mFrameAvailable) {
+        nsecs_t timeoutNsec = (nsecs_t)timeoutUsec * 1000;
+        int cc = mEventCond.waitRelative(mMutex, timeoutNsec);
+        if (cc == -ETIMEDOUT) {
+            ALOGV("cond wait timed out");
+            return ETIMEDOUT;
+        } else if (cc != 0) {
+            ALOGW("cond wait returned error %d", cc);
+            return cc;
+        }
+    }
+    if (!mFrameAvailable) {
+        // This happens when Ctrl-C is hit.  Apparently POSIX says that the
+        // pthread wait call doesn't return EINTR, treating this instead as
+        // an instance of a "spurious wakeup".  We didn't get a frame, so
+        // we just treat it as a timeout.
+        return ETIMEDOUT;
+    }
+
+    // A frame is available.  Clear the flag for the next round.
+    mFrameAvailable = false;
+
+    float texMatrix[16];
+    mGlConsumer->updateTexImage();
+    mGlConsumer->getTransformMatrix(texMatrix);
+
+    // The data is in an external texture, so we need to render it to the
+    // pbuffer to get access to RGB pixel data.  We also want to flip it
+    // upside-down for easy conversion to a bitmap.
+    int width = mEglWindow.getWidth();
+    int height = mEglWindow.getHeight();
+    status_t err = mExtTexProgram.blit(mExtTextureName, texMatrix, 0, 0,
+            width, height, true);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    // GLES only guarantees that glReadPixels() will work with GL_RGBA, so we
+    // need to get 4 bytes/pixel and reduce it.  Depending on the size of the
+    // screen and the device capabilities, this can take a while.
+    int64_t startWhenNsec, pixWhenNsec, endWhenNsec;
+    if (kShowTiming) {
+        startWhenNsec = systemTime(CLOCK_MONOTONIC);
+    }
+    GLenum glErr;
+    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, mPixelBuf);
+    if ((glErr = glGetError()) != GL_NO_ERROR) {
+        ALOGE("glReadPixels failed: %#x", glErr);
+        return UNKNOWN_ERROR;
+    }
+    if (kShowTiming) {
+        pixWhenNsec = systemTime(CLOCK_MONOTONIC);
+    }
+    reduceRgbaToRgb(mPixelBuf, width * height);
+    if (kShowTiming) {
+        endWhenNsec = systemTime(CLOCK_MONOTONIC);
+        ALOGD("got pixels (get=%.3f ms, reduce=%.3fms)",
+                (pixWhenNsec - startWhenNsec) / 1000000.0,
+                (endWhenNsec - pixWhenNsec) / 1000000.0);
+    }
+
+    // Fill out the header.
+    size_t headerLen = sizeof(uint32_t) * 5;
+    size_t rgbDataLen = width * height * kOutBytesPerPixel;
+    size_t packetLen = headerLen - sizeof(uint32_t) + rgbDataLen;
+    uint8_t header[headerLen];
+    setValueLE(&header[0], packetLen);
+    setValueLE(&header[4], width);
+    setValueLE(&header[8], height);
+    setValueLE(&header[12], width * kOutBytesPerPixel);
+    setValueLE(&header[16], HAL_PIXEL_FORMAT_RGB_888);
+
+    // Currently using buffered I/O rather than writev().  Not expecting it
+    // to make much of a difference, but it might be worth a test for larger
+    // frame sizes.
+    if (kShowTiming) {
+        startWhenNsec = systemTime(CLOCK_MONOTONIC);
+    }
+    fwrite(header, 1, headerLen, fp);
+    fwrite(mPixelBuf, 1, rgbDataLen, fp);
+    fflush(fp);
+    if (kShowTiming) {
+        endWhenNsec = systemTime(CLOCK_MONOTONIC);
+        ALOGD("wrote pixels (%.3f ms)",
+                (endWhenNsec - startWhenNsec) / 1000000.0);
+    }
+
+    if (ferror(fp)) {
+        // errno may not be useful; log it anyway
+        ALOGE("write failed (errno=%d)", errno);
+        return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+void FrameOutput::reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount) {
+    // Convert RGBA to RGB.
+    //
+    // Unaligned 32-bit accesses are allowed on ARM, so we could do this
+    // with 32-bit copies advancing at different rates (taking care at the
+    // end to not go one byte over).
+    const uint8_t* readPtr = buf;
+    for (unsigned int i = 0; i < pixelCount; i++) {
+        *buf++ = *readPtr++;
+        *buf++ = *readPtr++;
+        *buf++ = *readPtr++;
+        readPtr++;
+    }
+}
+
+// Callback; executes on arbitrary thread.
+void FrameOutput::onFrameAvailable() {
+    Mutex::Autolock _l(mMutex);
+    mFrameAvailable = true;
+    mEventCond.signal();
+}
diff --git a/cmds/screenrecord/FrameOutput.h b/cmds/screenrecord/FrameOutput.h
new file mode 100644
index 0000000..b8e9e68
--- /dev/null
+++ b/cmds/screenrecord/FrameOutput.h
@@ -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.
+ */
+
+#ifndef SCREENRECORD_FRAMEOUTPUT_H
+#define SCREENRECORD_FRAMEOUTPUT_H
+
+#include "Program.h"
+#include "EglWindow.h"
+
+#include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
+
+namespace android {
+
+/*
+ * Support for "frames" output format.
+ */
+class FrameOutput : public GLConsumer::FrameAvailableListener {
+public:
+    FrameOutput() : mFrameAvailable(false),
+        mExtTextureName(0),
+        mPixelBuf(NULL)
+        {}
+    virtual ~FrameOutput() {
+        delete[] mPixelBuf;
+    }
+
+    // Create an "input surface", similar in purpose to a MediaCodec input
+    // surface, that the virtual display can send buffers to.  Also configures
+    // EGL with a pbuffer surface on the current thread.
+    status_t createInputSurface(int width, int height,
+            sp<IGraphicBufferProducer>* pBufferProducer);
+
+    // Copy one from input to output.  If no frame is available, this will wait up to the
+    // specified number of microseconds.
+    //
+    // Returns ETIMEDOUT if the timeout expired before we found a frame.
+    status_t copyFrame(FILE* fp, long timeoutUsec);
+
+    // Prepare to copy frames.  Makes the EGL context used by this object current.
+    void prepareToCopy() {
+        mEglWindow.makeCurrent();
+    }
+
+private:
+    FrameOutput(const FrameOutput&);
+    FrameOutput& operator=(const FrameOutput&);
+
+    // (overrides GLConsumer::FrameAvailableListener method)
+    virtual void onFrameAvailable();
+
+    // Reduces RGBA to RGB, in place.
+    static void reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount);
+
+    // Put a 32-bit value into a buffer, in little-endian byte order.
+    static void setValueLE(uint8_t* buf, uint32_t value);
+
+    // Used to wait for the FrameAvailableListener callback.
+    Mutex mMutex;
+    Condition mEventCond;
+
+    // Set by the FrameAvailableListener callback.
+    bool mFrameAvailable;
+
+    // Our queue.  The producer side is passed to the virtual display, the
+    // consumer side feeds into our GLConsumer.
+    sp<BufferQueue> mBufferQueue;
+
+    // This receives frames from the virtual display and makes them available
+    // as an external texture.
+    sp<GLConsumer> mGlConsumer;
+
+    // EGL display / context / surface.
+    EglWindow mEglWindow;
+
+    // GL rendering support.
+    Program mExtTexProgram;
+
+    // External texture, updated by GLConsumer.
+    GLuint mExtTextureName;
+
+    // Pixel data buffer.
+    uint8_t* mPixelBuf;
+};
+
+}; // namespace android
+
+#endif /*SCREENRECORD_FRAMEOUTPUT_H*/
diff --git a/cmds/screenrecord/Program.cpp b/cmds/screenrecord/Program.cpp
index a198204..73cae6e 100644
--- a/cmds/screenrecord/Program.cpp
+++ b/cmds/screenrecord/Program.cpp
@@ -201,7 +201,7 @@
 
 
 status_t Program::blit(GLuint texName, const float* texMatrix,
-        int32_t x, int32_t y, int32_t w, int32_t h) const {
+        int32_t x, int32_t y, int32_t w, int32_t h, bool invert) const {
     ALOGV("Program::blit %d xy=%d,%d wh=%d,%d", texName, x, y, w, h);
 
     const float pos[] = {
@@ -218,7 +218,7 @@
     };
     status_t err;
 
-    err = beforeDraw(texName, texMatrix, pos, uv);
+    err = beforeDraw(texName, texMatrix, pos, uv, invert);
     if (err == NO_ERROR) {
         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
         err = afterDraw();
@@ -232,7 +232,7 @@
 
     status_t err;
 
-    err = beforeDraw(texName, texMatrix, vertices, texes);
+    err = beforeDraw(texName, texMatrix, vertices, texes, false);
     if (err == NO_ERROR) {
         glDrawArrays(GL_TRIANGLES, 0, count);
         err = afterDraw();
@@ -241,7 +241,7 @@
 }
 
 status_t Program::beforeDraw(GLuint texName, const float* texMatrix,
-        const float* vertices, const float* texes) const {
+        const float* vertices, const float* texes, bool invert) const {
     // Create an orthographic projection matrix based on viewport size.
     GLint vp[4];
     glGetIntegerv(GL_VIEWPORT, vp);
@@ -251,6 +251,10 @@
         0.0f,               0.0f,               1.0f,   0.0f,
         -1.0f,              1.0f,               0.0f,   1.0f,
     };
+    if (invert) {
+        screenToNdc[5] = -screenToNdc[5];
+        screenToNdc[13] = -screenToNdc[13];
+    }
 
     glUseProgram(mProgram);
 
diff --git a/cmds/screenrecord/Program.h b/cmds/screenrecord/Program.h
index e47bc0d..558be8d 100644
--- a/cmds/screenrecord/Program.h
+++ b/cmds/screenrecord/Program.h
@@ -51,9 +51,11 @@
     // Release the program and associated resources.
     void release();
 
-    // Blit the specified texture to { x, y, x+w, y+h }.
+    // Blit the specified texture to { x, y, x+w, y+h }.  Inverts the
+    // content if "invert" is set.
     status_t blit(GLuint texName, const float* texMatrix,
-            int32_t x, int32_t y, int32_t w, int32_t h) const;
+            int32_t x, int32_t y, int32_t w, int32_t h,
+            bool invert = false) const;
 
     // Draw a number of triangles.
     status_t drawTriangles(GLuint texName, const float* texMatrix,
@@ -67,7 +69,7 @@
 
     // Common code for draw functions.
     status_t beforeDraw(GLuint texName, const float* texMatrix,
-            const float* vertices, const float* texes) const;
+            const float* vertices, const float* texes, bool invert) const;
     status_t afterDraw() const;
 
     // GLES 2 shader utilities.
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index b6f150c..a17fc51 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -50,6 +50,7 @@
 
 #include "screenrecord.h"
 #include "Overlay.h"
+#include "FrameOutput.h"
 
 using namespace android;
 
@@ -58,11 +59,14 @@
 static const uint32_t kMaxTimeLimitSec = 180;       // 3 minutes
 static const uint32_t kFallbackWidth = 1280;        // 720p
 static const uint32_t kFallbackHeight = 720;
+static const char* kMimeTypeAvc = "video/avc";
 
 // Command-line parameters.
 static bool gVerbose = false;           // chatty on stdout
 static bool gRotate = false;            // rotate 90 degrees
-static bool gRawOutput = false;         // generate raw H.264 byte stream output
+static enum {
+    FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES
+} gOutputFormat = FORMAT_MP4;           // data format for output
 static bool gSizeSpecified = false;     // was size explicitly requested?
 static bool gWantInfoScreen = false;    // do we want initial info screen?
 static bool gWantFrameTime = false;     // do we want times on each frame?
@@ -142,14 +146,14 @@
     status_t err;
 
     if (gVerbose) {
-        printf("Configuring recorder for %dx%d video at %.2fMbps\n",
-                gVideoWidth, gVideoHeight, gBitRate / 1000000.0);
+        printf("Configuring recorder for %dx%d %s at %.2fMbps\n",
+                gVideoWidth, gVideoHeight, kMimeTypeAvc, gBitRate / 1000000.0);
     }
 
     sp<AMessage> format = new AMessage;
     format->setInt32("width", gVideoWidth);
     format->setInt32("height", gVideoHeight);
-    format->setString("mime", "video/avc");
+    format->setString("mime", kMimeTypeAvc);
     format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
     format->setInt32("bitrate", gBitRate);
     format->setFloat("frame-rate", displayFps);
@@ -159,16 +163,18 @@
     looper->setName("screenrecord_looper");
     looper->start();
     ALOGV("Creating codec");
-    sp<MediaCodec> codec = MediaCodec::CreateByType(looper, "video/avc", true);
+    sp<MediaCodec> codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true);
     if (codec == NULL) {
-        fprintf(stderr, "ERROR: unable to create video/avc codec instance\n");
+        fprintf(stderr, "ERROR: unable to create %s codec instance\n",
+                kMimeTypeAvc);
         return UNKNOWN_ERROR;
     }
 
     err = codec->configure(format, NULL, NULL,
             MediaCodec::CONFIGURE_FLAG_ENCODE);
     if (err != NO_ERROR) {
-        fprintf(stderr, "ERROR: unable to configure codec (err=%d)\n", err);
+        fprintf(stderr, "ERROR: unable to configure %s codec at %dx%d (err=%d)\n",
+                kMimeTypeAvc, gVideoWidth, gVideoHeight, err);
         codec->release();
         return err;
     }
@@ -513,7 +519,7 @@
 }
 
 /*
- * Main "do work" method.
+ * Main "do work" start point.
  *
  * Configures codec, muxer, and virtual display, then starts moving bits
  * around.
@@ -555,30 +561,40 @@
 
     // Configure and start the encoder.
     sp<MediaCodec> encoder;
+    sp<FrameOutput> frameOutput;
     sp<IGraphicBufferProducer> encoderInputSurface;
-    err = prepareEncoder(mainDpyInfo.fps, &encoder, &encoderInputSurface);
+    if (gOutputFormat != FORMAT_FRAMES) {
+        err = prepareEncoder(mainDpyInfo.fps, &encoder, &encoderInputSurface);
 
-    if (err != NO_ERROR && !gSizeSpecified) {
-        // fallback is defined for landscape; swap if we're in portrait
-        bool needSwap = gVideoWidth < gVideoHeight;
-        uint32_t newWidth = needSwap ? kFallbackHeight : kFallbackWidth;
-        uint32_t newHeight = needSwap ? kFallbackWidth : kFallbackHeight;
-        if (gVideoWidth != newWidth && gVideoHeight != newHeight) {
-            ALOGV("Retrying with 720p");
-            fprintf(stderr, "WARNING: failed at %dx%d, retrying at %dx%d\n",
-                    gVideoWidth, gVideoHeight, newWidth, newHeight);
-            gVideoWidth = newWidth;
-            gVideoHeight = newHeight;
-            err = prepareEncoder(mainDpyInfo.fps, &encoder,
-                    &encoderInputSurface);
+        if (err != NO_ERROR && !gSizeSpecified) {
+            // fallback is defined for landscape; swap if we're in portrait
+            bool needSwap = gVideoWidth < gVideoHeight;
+            uint32_t newWidth = needSwap ? kFallbackHeight : kFallbackWidth;
+            uint32_t newHeight = needSwap ? kFallbackWidth : kFallbackHeight;
+            if (gVideoWidth != newWidth && gVideoHeight != newHeight) {
+                ALOGV("Retrying with 720p");
+                fprintf(stderr, "WARNING: failed at %dx%d, retrying at %dx%d\n",
+                        gVideoWidth, gVideoHeight, newWidth, newHeight);
+                gVideoWidth = newWidth;
+                gVideoHeight = newHeight;
+                err = prepareEncoder(mainDpyInfo.fps, &encoder,
+                        &encoderInputSurface);
+            }
+        }
+        if (err != NO_ERROR) return err;
+
+        // From here on, we must explicitly release() the encoder before it goes
+        // out of scope, or we will get an assertion failure from stagefright
+        // later on in a different thread.
+    } else {
+        // We're not using an encoder at all.  The "encoder input surface" we hand to
+        // SurfaceFlinger will just feed directly to us.
+        frameOutput = new FrameOutput();
+        err = frameOutput->createInputSurface(gVideoWidth, gVideoHeight, &encoderInputSurface);
+        if (err != NO_ERROR) {
+            return err;
         }
     }
-    if (err != NO_ERROR) return err;
-
-    // From here on, we must explicitly release() the encoder before it goes
-    // out of scope, or we will get an assertion failure from stagefright
-    // later on in a different thread.
-
 
     // Draw the "info" page by rendering a frame with GLES and sending
     // it directly to the encoder.
@@ -595,7 +611,7 @@
         overlay = new Overlay();
         err = overlay->start(encoderInputSurface, &bufferProducer);
         if (err != NO_ERROR) {
-            encoder->release();
+            if (encoder != NULL) encoder->release();
             return err;
         }
         if (gVerbose) {
@@ -610,46 +626,83 @@
     sp<IBinder> dpy;
     err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy);
     if (err != NO_ERROR) {
-        encoder->release();
+        if (encoder != NULL) encoder->release();
         return err;
     }
 
     sp<MediaMuxer> muxer = NULL;
     FILE* rawFp = NULL;
-    if (gRawOutput) {
-        rawFp = prepareRawOutput(fileName);
-        if (rawFp == NULL) {
-            encoder->release();
-            return -1;
+    switch (gOutputFormat) {
+        case FORMAT_MP4: {
+            // Configure muxer.  We have to wait for the CSD blob from the encoder
+            // before we can start it.
+            muxer = new MediaMuxer(fileName, MediaMuxer::OUTPUT_FORMAT_MPEG_4);
+            if (gRotate) {
+                muxer->setOrientationHint(90);  // TODO: does this do anything?
+            }
+            break;
+        }
+        case FORMAT_H264:
+        case FORMAT_FRAMES: {
+            rawFp = prepareRawOutput(fileName);
+            if (rawFp == NULL) {
+                if (encoder != NULL) encoder->release();
+                return -1;
+            }
+            break;
+        }
+        default:
+            fprintf(stderr, "ERROR: unknown format %d\n", gOutputFormat);
+            abort();
+    }
+
+    if (gOutputFormat == FORMAT_FRAMES) {
+        // TODO: if we want to make this a proper feature, we should output
+        //       an outer header with version info.  Right now we never change
+        //       the frame size or format, so we could conceivably just send
+        //       the current frame header once and then follow it with an
+        //       unbroken stream of data.
+
+        // Make the EGL context current again.  This gets unhooked if we're
+        // using "--bugreport" mode.
+        // TODO: figure out if we can eliminate this
+        frameOutput->prepareToCopy();
+
+        while (!gStopRequested) {
+            // Poll for frames, the same way we do for MediaCodec.  We do
+            // all of the work on the main thread.
+            //
+            // Ideally we'd sleep indefinitely and wake when the
+            // stop was requested, but this will do for now.  (It almost
+            // works because wait() wakes when a signal hits, but we
+            // need to handle the edge cases.)
+            err = frameOutput->copyFrame(rawFp, 250000);
+            if (err == ETIMEDOUT) {
+                err = NO_ERROR;
+            } else if (err != NO_ERROR) {
+                ALOGE("Got error %d from copyFrame()", err);
+                break;
+            }
         }
     } else {
-        // Configure muxer.  We have to wait for the CSD blob from the encoder
-        // before we can start it.
-        muxer = new MediaMuxer(fileName, MediaMuxer::OUTPUT_FORMAT_MPEG_4);
-        if (gRotate) {
-            muxer->setOrientationHint(90);  // TODO: does this do anything?
+        // Main encoder loop.
+        err = runEncoder(encoder, muxer, rawFp, mainDpy, dpy,
+                mainDpyInfo.orientation);
+        if (err != NO_ERROR) {
+            fprintf(stderr, "Encoder failed (err=%d)\n", err);
+            // fall through to cleanup
         }
-    }
 
-    // Main encoder loop.
-    err = runEncoder(encoder, muxer, rawFp, mainDpy, dpy,
-            mainDpyInfo.orientation);
-    if (err != NO_ERROR) {
-        fprintf(stderr, "Encoder failed (err=%d)\n", err);
-        // fall through to cleanup
-    }
-
-    if (gVerbose) {
-        printf("Stopping encoder and muxer\n");
+        if (gVerbose) {
+            printf("Stopping encoder and muxer\n");
+        }
     }
 
     // Shut everything down, starting with the producer side.
     encoderInputSurface = NULL;
     SurfaceComposerClient::destroyDisplay(dpy);
-    if (overlay != NULL) {
-        overlay->stop();
-    }
-    encoder->stop();
+    if (overlay != NULL) overlay->stop();
+    if (encoder != NULL) encoder->stop();
     if (muxer != NULL) {
         // If we don't stop muxer explicitly, i.e. let the destructor run,
         // it may hang (b/11050628).
@@ -657,7 +710,7 @@
     } else if (rawFp != stdout) {
         fclose(rawFp);
     }
-    encoder->release();
+    if (encoder != NULL) encoder->release();
 
     return err;
 }
@@ -819,11 +872,12 @@
         { "size",               required_argument,  NULL, 's' },
         { "bit-rate",           required_argument,  NULL, 'b' },
         { "time-limit",         required_argument,  NULL, 't' },
+        { "bugreport",          no_argument,        NULL, 'u' },
+        // "unofficial" options
         { "show-device-info",   no_argument,        NULL, 'i' },
         { "show-frame-time",    no_argument,        NULL, 'f' },
-        { "bugreport",          no_argument,        NULL, 'u' },
         { "rotate",             no_argument,        NULL, 'r' },
-        { "raw",                no_argument,        NULL, 'w' },
+        { "output-format",      required_argument,  NULL, 'o' },
         { NULL,                 0,                  NULL, 0 }
     };
 
@@ -875,23 +929,31 @@
                 return 2;
             }
             break;
+        case 'u':
+            gWantInfoScreen = true;
+            gWantFrameTime = true;
+            break;
         case 'i':
             gWantInfoScreen = true;
             break;
         case 'f':
             gWantFrameTime = true;
             break;
-        case 'u':
-            gWantInfoScreen = true;
-            gWantFrameTime = true;
-            break;
         case 'r':
             // experimental feature
             gRotate = true;
             break;
-        case 'w':
-            // experimental feature
-            gRawOutput = true;
+        case 'o':
+            if (strcmp(optarg, "mp4") == 0) {
+                gOutputFormat = FORMAT_MP4;
+            } else if (strcmp(optarg, "h264") == 0) {
+                gOutputFormat = FORMAT_H264;
+            } else if (strcmp(optarg, "frames") == 0) {
+                gOutputFormat = FORMAT_FRAMES;
+            } else {
+                fprintf(stderr, "Unknown format '%s'\n", optarg);
+                return 2;
+            }
             break;
         default:
             if (ic != '?') {
@@ -907,7 +969,7 @@
     }
 
     const char* fileName = argv[optind];
-    if (!gRawOutput) {
+    if (gOutputFormat == FORMAT_MP4) {
         // MediaMuxer tries to create the file in the constructor, but we don't
         // learn about the failure until muxer.start(), which returns a generic
         // error code without logging anything.  We attempt to create the file
diff --git a/drm/mediadrm/plugins/mock/Android.mk b/drm/mediadrm/plugins/mock/Android.mk
index ada23a2..26c245b 100644
--- a/drm/mediadrm/plugins/mock/Android.mk
+++ b/drm/mediadrm/plugins/mock/Android.mk
@@ -21,7 +21,8 @@
 
 LOCAL_MODULE := libmockdrmcryptoplugin
 
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/mediadrm
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := mediadrm
 
 LOCAL_SHARED_LIBRARIES := \
     libutils liblog
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 7054fd4..b3c44a8 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -158,10 +158,10 @@
                                     uint32_t sampleRate,
                                     audio_format_t format,
                                     audio_channel_mask_t channelMask,
-                                    int frameCount      = 0,
+                                    size_t frameCount = 0,
                                     callback_t cbf = NULL,
                                     void* user = NULL,
-                                    int notificationFrames = 0,
+                                    uint32_t notificationFrames = 0,
                                     int sessionId = AUDIO_SESSION_ALLOCATE,
                                     transfer_type transferType = TRANSFER_DEFAULT,
                                     audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE);
@@ -191,10 +191,10 @@
                             uint32_t sampleRate,
                             audio_format_t format,
                             audio_channel_mask_t channelMask,
-                            int frameCount      = 0,
+                            size_t frameCount = 0,
                             callback_t cbf = NULL,
                             void* user = NULL,
-                            int notificationFrames = 0,
+                            uint32_t notificationFrames = 0,
                             bool threadCanCallJava = false,
                             int sessionId = AUDIO_SESSION_ALLOCATE,
                             transfer_type transferType = TRANSFER_DEFAULT,
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index fd86737..28fdfd4 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -118,6 +118,7 @@
 
     static bool routedToA2dpOutput(audio_stream_type_t streamType);
 
+    // return status NO_ERROR implies *buffSize > 0
     static status_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
         audio_channel_mask_t channelMask, size_t* buffSize);
 
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index d0df710..7e9d557 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -182,11 +182,11 @@
                                     uint32_t sampleRate,
                                     audio_format_t format,
                                     audio_channel_mask_t,
-                                    int frameCount       = 0,
+                                    size_t frameCount    = 0,
                                     audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                                     callback_t cbf       = NULL,
                                     void* user           = NULL,
-                                    int notificationFrames = 0,
+                                    uint32_t notificationFrames = 0,
                                     int sessionId        = AUDIO_SESSION_ALLOCATE,
                                     transfer_type transferType = TRANSFER_DEFAULT,
                                     const audio_offload_info_t *offloadInfo = NULL,
@@ -212,7 +212,7 @@
                                     audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                                     callback_t cbf      = NULL,
                                     void* user          = NULL,
-                                    int notificationFrames = 0,
+                                    uint32_t notificationFrames = 0,
                                     int sessionId       = AUDIO_SESSION_ALLOCATE,
                                     transfer_type transferType = TRANSFER_DEFAULT,
                                     const audio_offload_info_t *offloadInfo = NULL,
@@ -245,11 +245,11 @@
                             uint32_t sampleRate,
                             audio_format_t format,
                             audio_channel_mask_t channelMask,
-                            int frameCount      = 0,
+                            size_t frameCount   = 0,
                             audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                             callback_t cbf      = NULL,
                             void* user          = NULL,
-                            int notificationFrames = 0,
+                            uint32_t notificationFrames = 0,
                             const sp<IMemory>& sharedBuffer = 0,
                             bool threadCanCallJava = false,
                             int sessionId       = AUDIO_SESSION_ALLOCATE,
@@ -284,7 +284,7 @@
             size_t      frameSize() const   { return mFrameSize; }
 
             uint32_t    channelCount() const { return mChannelCount; }
-            uint32_t    frameCount() const  { return mFrameCount; }
+            size_t      frameCount() const  { return mFrameCount; }
 
     /* Return the static buffer specified in constructor or set(), or 0 for streaming mode */
             sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 7f53bfc..7c5f33a 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -169,7 +169,7 @@
                                         audio_channel_mask_t *pChannelMask) = 0;
     virtual status_t closeInput(audio_io_handle_t input) = 0;
 
-    virtual status_t setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output) = 0;
+    virtual status_t invalidateStream(audio_stream_type_t stream) = 0;
 
     virtual status_t setVoiceVolume(float volume) = 0;
 
diff --git a/libvideoeditor/lvpp/Android.mk b/libvideoeditor/lvpp/Android.mk
index 2286827..860d351 100755
--- a/libvideoeditor/lvpp/Android.mk
+++ b/libvideoeditor/lvpp/Android.mk
@@ -71,7 +71,6 @@
     $(TOP)/frameworks/av/media/libstagefright \
     $(TOP)/frameworks/av/media/libstagefright/include \
     $(TOP)/frameworks/av/media/libstagefright/rtsp \
-    $(call include-path-for, corecg graphics) \
     $(TOP)/frameworks/av/libvideoeditor/osal/inc \
     $(TOP)/frameworks/av/libvideoeditor/vss/common/inc \
     $(TOP)/frameworks/av/libvideoeditor/vss/mcs/inc \
diff --git a/libvideoeditor/vss/stagefrightshells/src/Android.mk b/libvideoeditor/vss/stagefrightshells/src/Android.mk
index e30b85d..9188942 100755
--- a/libvideoeditor/vss/stagefrightshells/src/Android.mk
+++ b/libvideoeditor/vss/stagefrightshells/src/Android.mk
@@ -33,7 +33,6 @@
     $(TOP)/frameworks/av/media/libstagefright \
     $(TOP)/frameworks/av/media/libstagefright/include \
     $(TOP)/frameworks/av/media/libstagefright/rtsp \
-    $(call include-path-for, corecg graphics) \
     $(TOP)/frameworks/av/libvideoeditor/lvpp \
     $(TOP)/frameworks/av/libvideoeditor/osal/inc \
     $(TOP)/frameworks/av/libvideoeditor/vss/inc \
diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c
index 4ee05f2..d25dc9b 100644
--- a/media/libeffects/downmix/EffectDownmix.c
+++ b/media/libeffects/downmix/EffectDownmix.c
@@ -629,7 +629,9 @@
         return -EINVAL;
     }
 
-    memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
+    if (&pDwmModule->config != pConfig) {
+        memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
+    }
 
     if (init) {
         pDownmixer->type = DOWNMIX_TYPE_FOLD;
diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk
index dd2d306..c92c543 100644
--- a/media/libeffects/visualizer/Android.mk
+++ b/media/libeffects/visualizer/Android.mk
@@ -17,7 +17,6 @@
 LOCAL_MODULE:= libvisualizer
 
 LOCAL_C_INCLUDES := \
-	$(call include-path-for, graphics corecg) \
 	$(call include-path-for, audio-effects)
 
 
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 2d66eef..5bdaa03 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -544,56 +544,57 @@
         break;
 
 
-    case VISUALIZER_CMD_CAPTURE:
-        if (pReplyData == NULL || *replySize != pContext->mCaptureSize) {
-            ALOGV("VISUALIZER_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
-                    *replySize, pContext->mCaptureSize);
+    case VISUALIZER_CMD_CAPTURE: {
+        int32_t captureSize = pContext->mCaptureSize;
+        if (pReplyData == NULL || *replySize != captureSize) {
+            ALOGV("VISUALIZER_CMD_CAPTURE() error *replySize %d captureSize %d",
+                    *replySize, captureSize);
             return -EINVAL;
         }
         if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
-            int32_t latencyMs = pContext->mLatency;
             const uint32_t deltaMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext);
-            latencyMs -= deltaMs;
-            if (latencyMs < 0) {
-                latencyMs = 0;
-            }
-            const uint32_t deltaSmpl = pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000;
-
-            int32_t capturePoint = pContext->mCaptureIdx - pContext->mCaptureSize - deltaSmpl;
-            int32_t captureSize = pContext->mCaptureSize;
-            if (capturePoint < 0) {
-                int32_t size = -capturePoint;
-                if (size > captureSize) {
-                    size = captureSize;
-                }
-                memcpy(pReplyData,
-                       pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint,
-                       size);
-                pReplyData = (char *)pReplyData + size;
-                captureSize -= size;
-                capturePoint = 0;
-            }
-            memcpy(pReplyData,
-                   pContext->mCaptureBuf + capturePoint,
-                   captureSize);
-
 
             // if audio framework has stopped playing audio although the effect is still
             // active we must clear the capture buffer to return silence
             if ((pContext->mLastCaptureIdx == pContext->mCaptureIdx) &&
-                    (pContext->mBufferUpdateTime.tv_sec != 0)) {
-                if (deltaMs > MAX_STALL_TIME_MS) {
+                    (pContext->mBufferUpdateTime.tv_sec != 0) &&
+                    (deltaMs > MAX_STALL_TIME_MS)) {
                     ALOGV("capture going to idle");
                     pContext->mBufferUpdateTime.tv_sec = 0;
-                    memset(pReplyData, 0x80, pContext->mCaptureSize);
+                    memset(pReplyData, 0x80, captureSize);
+            } else {
+                int32_t latencyMs = pContext->mLatency;
+                latencyMs -= deltaMs;
+                if (latencyMs < 0) {
+                    latencyMs = 0;
                 }
+                const uint32_t deltaSmpl =
+                    pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000;
+                int32_t capturePoint = pContext->mCaptureIdx - captureSize - deltaSmpl;
+
+                if (capturePoint < 0) {
+                    int32_t size = -capturePoint;
+                    if (size > captureSize) {
+                        size = captureSize;
+                    }
+                    memcpy(pReplyData,
+                           pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint,
+                           size);
+                    pReplyData = (char *)pReplyData + size;
+                    captureSize -= size;
+                    capturePoint = 0;
+                }
+                memcpy(pReplyData,
+                       pContext->mCaptureBuf + capturePoint,
+                       captureSize);
             }
+
             pContext->mLastCaptureIdx = pContext->mCaptureIdx;
         } else {
-            memset(pReplyData, 0x80, pContext->mCaptureSize);
+            memset(pReplyData, 0x80, captureSize);
         }
 
-        break;
+        } break;
 
     case VISUALIZER_CMD_MEASURE: {
         uint16_t peakU16 = 0;
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index e0acae6..f3770e4 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -72,7 +72,6 @@
 LOCAL_MODULE:= libmedia
 
 LOCAL_C_INCLUDES := \
-    $(call include-path-for, graphics corecg) \
     $(TOP)/frameworks/native/include/media/openmax \
     external/icu4c/common \
     external/icu4c/i18n \
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 700718d..a8e1f5d 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -41,30 +41,22 @@
         return BAD_VALUE;
     }
 
-    // default to 0 in case of error
-    *frameCount = 0;
-
-    size_t size = 0;
+    size_t size;
     status_t status = AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size);
     if (status != NO_ERROR) {
-        ALOGE("AudioSystem could not query the input buffer size; status %d", status);
-        return NO_INIT;
+        ALOGE("AudioSystem could not query the input buffer size for sampleRate %u, format %#x, "
+              "channelMask %#x; status %d", sampleRate, format, channelMask, status);
+        return status;
     }
 
-    if (size == 0) {
+    // We double the size of input buffer for ping pong use of record buffer.
+    // Assumes audio_is_linear_pcm(format)
+    if ((*frameCount = (size * 2) / (popcount(channelMask) * audio_bytes_per_sample(format))) == 0) {
         ALOGE("Unsupported configuration: sampleRate %u, format %#x, channelMask %#x",
             sampleRate, format, channelMask);
         return BAD_VALUE;
     }
 
-    // We double the size of input buffer for ping pong use of record buffer.
-    size <<= 1;
-
-    // Assumes audio_is_linear_pcm(format)
-    uint32_t channelCount = popcount(channelMask);
-    size /= channelCount * audio_bytes_per_sample(format);
-
-    *frameCount = size;
     return NO_ERROR;
 }
 
@@ -81,10 +73,10 @@
         uint32_t sampleRate,
         audio_format_t format,
         audio_channel_mask_t channelMask,
-        int frameCount,
+        size_t frameCount,
         callback_t cbf,
         void* user,
-        int notificationFrames,
+        uint32_t notificationFrames,
         int sessionId,
         transfer_type transferType,
         audio_input_flags_t flags __unused)
@@ -110,10 +102,8 @@
             mAudioRecordThread->requestExitAndWait();
             mAudioRecordThread.clear();
         }
-        if (mAudioRecord != 0) {
-            mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
-            mAudioRecord.clear();
-        }
+        mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
+        mAudioRecord.clear();
         IPCThreadState::self()->flushCommands();
         AudioSystem::releaseAudioSessionId(mSessionId, -1);
     }
@@ -124,15 +114,20 @@
         uint32_t sampleRate,
         audio_format_t format,
         audio_channel_mask_t channelMask,
-        int frameCountInt,
+        size_t frameCount,
         callback_t cbf,
         void* user,
-        int notificationFrames,
+        uint32_t notificationFrames,
         bool threadCanCallJava,
         int sessionId,
         transfer_type transferType,
         audio_input_flags_t flags)
 {
+    ALOGV("set(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
+          "notificationFrames %u, sessionId %d, transferType %d, flags %#x",
+          inputSource, sampleRate, format, channelMask, frameCount, notificationFrames,
+          sessionId, transferType, flags);
+
     switch (transferType) {
     case TRANSFER_DEFAULT:
         if (cbf == NULL || threadCanCallJava) {
@@ -156,23 +151,15 @@
     }
     mTransfer = transferType;
 
-    // FIXME "int" here is legacy and will be replaced by size_t later
-    if (frameCountInt < 0) {
-        ALOGE("Invalid frame count %d", frameCountInt);
-        return BAD_VALUE;
-    }
-    size_t frameCount = frameCountInt;
-
-    ALOGV("set(): sampleRate %u, channelMask %#x, frameCount %u", sampleRate, channelMask,
-            frameCount);
-
     AutoMutex lock(mLock);
 
+    // invariant that mAudioRecord != 0 is true only after set() returns successfully
     if (mAudioRecord != 0) {
         ALOGE("Track already in use");
         return INVALID_OPERATION;
     }
 
+    // handle default values first.
     if (inputSource == AUDIO_SOURCE_DEFAULT) {
         inputSource = AUDIO_SOURCE_MIC;
     }
@@ -209,15 +196,19 @@
     uint32_t channelCount = popcount(channelMask);
     mChannelCount = channelCount;
 
-    // Assumes audio_is_linear_pcm(format), else sizeof(uint8_t)
-    mFrameSize = channelCount * audio_bytes_per_sample(format);
+    if (audio_is_linear_pcm(format)) {
+        mFrameSize = channelCount * audio_bytes_per_sample(format);
+    } else {
+        mFrameSize = sizeof(uint8_t);
+    }
 
     // validate framecount
-    size_t minFrameCount = 0;
+    size_t minFrameCount;
     status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
             sampleRate, format, channelMask);
     if (status != NO_ERROR) {
-        ALOGE("getMinFrameCount() failed; status %d", status);
+        ALOGE("getMinFrameCount() failed for sampleRate %u, format %#x, channelMask %#x; status %d",
+                sampleRate, format, channelMask, status);
         return status;
     }
     ALOGV("AudioRecord::set() minFrameCount = %d", minFrameCount);
@@ -258,7 +249,6 @@
 
     mActive = false;
     mCbf = cbf;
-    mRefreshRemaining = true;
     mUserData = user;
     // TODO: add audio hardware input latency here
     mLatency = (1000*mFrameCount) / sampleRate;
@@ -433,22 +423,37 @@
         return NO_INIT;
     }
 
-    IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
-    pid_t tid = -1;
+    // Fast tracks must be at the primary _output_ [sic] sampling rate,
+    // because there is currently no concept of a primary input sampling rate
+    uint32_t afSampleRate = AudioSystem::getPrimaryOutputSamplingRate();
+    if (afSampleRate == 0) {
+        ALOGW("getPrimaryOutputSamplingRate failed");
+    }
 
     // Client can only express a preference for FAST.  Server will perform additional tests.
-    // The only supported use case for FAST is callback transfer mode.
+    if ((mFlags & AUDIO_INPUT_FLAG_FAST) && !(
+            // use case: callback transfer mode
+            (mTransfer == TRANSFER_CALLBACK) &&
+            // matching sample rate
+            (mSampleRate == afSampleRate))) {
+        ALOGW("AUDIO_INPUT_FLAG_FAST denied by client");
+        // once denied, do not request again if IAudioRecord is re-created
+        mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
+    }
+
+    IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
+
+    pid_t tid = -1;
     if (mFlags & AUDIO_INPUT_FLAG_FAST) {
-        if ((mTransfer != TRANSFER_CALLBACK) || (mAudioRecordThread == 0)) {
-            ALOGW("AUDIO_INPUT_FLAG_FAST denied by client");
-            // once denied, do not request again if IAudioRecord is re-created
-            mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
-        } else {
-            trackFlags |= IAudioFlinger::TRACK_FAST;
+        trackFlags |= IAudioFlinger::TRACK_FAST;
+        if (mAudioRecordThread != 0) {
             tid = mAudioRecordThread->getTid();
         }
     }
 
+    // FIXME Assume double buffering, because we don't know the true HAL sample rate
+    const uint32_t nBuffering = 2;
+
     mNotificationFramesAct = mNotificationFramesReq;
     size_t frameCount = mReqFrameCount;
 
@@ -485,10 +490,12 @@
     ALOGE_IF(originalSessionId != AUDIO_SESSION_ALLOCATE && mSessionId != originalSessionId,
             "session ID changed from %d to %d", originalSessionId, mSessionId);
 
-    if (record == 0 || status != NO_ERROR) {
+    if (status != NO_ERROR) {
         ALOGE("AudioFlinger could not create record track, status: %d", status);
         goto release;
     }
+    ALOG_ASSERT(record != 0);
+
     // AudioFlinger now owns the reference to the I/O handle,
     // so we are no longer responsible for releasing it.
 
@@ -502,53 +509,56 @@
         ALOGE("Could not get control block pointer");
         return NO_INIT;
     }
+    // invariant that mAudioRecord != 0 is true only after set() returns successfully
     if (mAudioRecord != 0) {
         mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
         mDeathNotifier.clear();
     }
-
-    // We retain a copy of the I/O handle, but don't own the reference
-    mInput = input;
     mAudioRecord = record;
+
     mCblkMemory = iMem;
     audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
     mCblk = cblk;
-    // note that temp is the (possibly revised) value of mFrameCount
+    // note that temp is the (possibly revised) value of frameCount
     if (temp < frameCount || (frameCount == 0 && temp == 0)) {
         ALOGW("Requested frameCount %u but received frameCount %u", frameCount, temp);
     }
     frameCount = temp;
+
+    mAwaitBoost = false;
+    if (mFlags & AUDIO_INPUT_FLAG_FAST) {
+        if (trackFlags & IAudioFlinger::TRACK_FAST) {
+            ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %u", frameCount);
+            mAwaitBoost = true;
+        } else {
+            ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %u", frameCount);
+            // once denied, do not request again if IAudioRecord is re-created
+            mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
+        }
+        // Theoretically double-buffering is not required for fast tracks,
+        // due to tighter scheduling.  But in practice, to accomodate kernels with
+        // scheduling jitter, and apps with computation jitter, we use double-buffering.
+        if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) {
+            mNotificationFramesAct = frameCount/nBuffering;
+        }
+    }
+
+    // We retain a copy of the I/O handle, but don't own the reference
+    mInput = input;
+    mRefreshRemaining = true;
+
+    // Starting address of buffers in shared memory, immediately after the control block.  This
+    // address is for the mapping within client address space.  AudioFlinger::TrackBase::mBuffer
+    // is for the server address space.
+    void *buffers = (char*)cblk + sizeof(audio_track_cblk_t);
+
+    mFrameCount = frameCount;
     // If IAudioRecord is re-created, don't let the requested frameCount
     // decrease.  This can confuse clients that cache frameCount().
     if (frameCount > mReqFrameCount) {
         mReqFrameCount = frameCount;
     }
 
-    // FIXME missing fast track frameCount logic
-    mAwaitBoost = false;
-    if (mFlags & AUDIO_INPUT_FLAG_FAST) {
-        if (trackFlags & IAudioFlinger::TRACK_FAST) {
-            ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %u", mFrameCount);
-            mAwaitBoost = true;
-            // double-buffering is not required for fast tracks, due to tighter scheduling
-            if (mNotificationFramesAct == 0 || mNotificationFramesAct > mFrameCount) {
-                mNotificationFramesAct = mFrameCount;
-            }
-        } else {
-            ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %u", mFrameCount);
-            // once denied, do not request again if IAudioRecord is re-created
-            mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
-            if (mNotificationFramesAct == 0 || mNotificationFramesAct > mFrameCount/2) {
-                mNotificationFramesAct = mFrameCount/2;
-            }
-        }
-    }
-
-    // starting address of buffers in shared memory
-    void *buffers = (char*)cblk + sizeof(audio_track_cblk_t);
-
-    mFrameCount = frameCount;
-
     // update proxy
     mProxy = new AudioRecordClientProxy(cblk, buffers, mFrameCount, mFrameSize);
     mProxy->setEpoch(epoch);
@@ -799,7 +809,7 @@
     }
 
     // Cache other fields that will be needed soon
-    size_t notificationFrames = mNotificationFramesAct;
+    uint32_t notificationFrames = mNotificationFramesAct;
     if (mRefreshRemaining) {
         mRefreshRemaining = false;
         mRemainingFrames = notificationFrames;
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 5c62260..d25c40b 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -108,11 +108,11 @@
         uint32_t sampleRate,
         audio_format_t format,
         audio_channel_mask_t channelMask,
-        int frameCount,
+        size_t frameCount,
         audio_output_flags_t flags,
         callback_t cbf,
         void* user,
-        int notificationFrames,
+        uint32_t notificationFrames,
         int sessionId,
         transfer_type transferType,
         const audio_offload_info_t *offloadInfo,
@@ -138,7 +138,7 @@
         audio_output_flags_t flags,
         callback_t cbf,
         void* user,
-        int notificationFrames,
+        uint32_t notificationFrames,
         int sessionId,
         transfer_type transferType,
         const audio_offload_info_t *offloadInfo,
@@ -182,11 +182,11 @@
         uint32_t sampleRate,
         audio_format_t format,
         audio_channel_mask_t channelMask,
-        int frameCountInt,
+        size_t frameCount,
         audio_output_flags_t flags,
         callback_t cbf,
         void* user,
-        int notificationFrames,
+        uint32_t notificationFrames,
         const sp<IMemory>& sharedBuffer,
         bool threadCanCallJava,
         int sessionId,
@@ -195,6 +195,11 @@
         int uid,
         pid_t pid)
 {
+    ALOGV("set(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
+          "flags #%x, notificationFrames %u, sessionId %d, transferType %d",
+          streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
+          sessionId, transferType);
+
     switch (transferType) {
     case TRANSFER_DEFAULT:
         if (sharedBuffer != 0) {
@@ -231,13 +236,6 @@
     mSharedBuffer = sharedBuffer;
     mTransfer = transferType;
 
-    // FIXME "int" here is legacy and will be replaced by size_t later
-    if (frameCountInt < 0) {
-        ALOGE("Invalid frame count %d", frameCountInt);
-        return BAD_VALUE;
-    }
-    size_t frameCount = frameCountInt;
-
     ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(),
             sharedBuffer->size());
 
@@ -288,6 +286,9 @@
         ALOGE("Invalid channel mask %#x", channelMask);
         return BAD_VALUE;
     }
+    mChannelMask = channelMask;
+    uint32_t channelCount = popcount(channelMask);
+    mChannelCount = channelCount;
 
     // AudioFlinger does not currently support 8-bit data in shared memory
     if (format == AUDIO_FORMAT_PCM_8_BIT && sharedBuffer != 0) {
@@ -311,10 +312,6 @@
         flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);
     }
 
-    mChannelMask = channelMask;
-    uint32_t channelCount = popcount(channelMask);
-    mChannelCount = channelCount;
-
     if (audio_is_linear_pcm(format)) {
         mFrameSize = channelCount * audio_bytes_per_sample(format);
         mFrameSizeAF = channelCount * sizeof(int16_t);
@@ -888,8 +885,8 @@
             // either of these use cases:
             // use case 1: shared buffer
             (mSharedBuffer != 0) ||
-            // use case 2: callback handler
-            (mCbf != NULL)) &&
+            // use case 2: callback transfer mode
+            (mTransfer == TRANSFER_CALLBACK)) &&
             // matching sample rate
             (mSampleRate == afSampleRate))) {
         ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client");
@@ -1012,10 +1009,12 @@
                                                       mClientUid,
                                                       &status);
 
-    if (track == 0) {
+    if (status != NO_ERROR) {
         ALOGE("AudioFlinger could not create track, status: %d", status);
         goto release;
     }
+    ALOG_ASSERT(track != 0);
+
     // AudioFlinger now owns the reference to the I/O handle,
     // so we are no longer responsible for releasing it.
 
@@ -1035,6 +1034,7 @@
         mDeathNotifier.clear();
     }
     mAudioTrack = track;
+
     mCblkMemory = iMem;
     audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
     mCblk = cblk;
@@ -1046,6 +1046,7 @@
         ALOGW("Requested frameCount %u but received frameCount %u", frameCount, temp);
     }
     frameCount = temp;
+
     mAwaitBoost = false;
     if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
         if (trackFlags & IAudioFlinger::TRACK_FAST) {
@@ -1099,6 +1100,7 @@
     mAudioTrack->attachAuxEffect(mAuxEffectId);
     // FIXME don't believe this lie
     mLatency = afLatency + (1000*frameCount) / mSampleRate;
+
     mFrameCount = frameCount;
     // If IAudioTrack is re-created, don't let the requested frameCount
     // decrease.  This can confuse clients that cache frameCount().
@@ -1478,7 +1480,7 @@
     // Cache other fields that will be needed soon
     uint32_t loopPeriod = mLoopPeriod;
     uint32_t sampleRate = mSampleRate;
-    size_t notificationFrames = mNotificationFramesAct;
+    uint32_t notificationFrames = mNotificationFramesAct;
     if (mRefreshRemaining) {
         mRefreshRemaining = false;
         mRemainingFrames = notificationFrames;
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index e696323..a9a9f1a 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -58,7 +58,7 @@
     RESTORE_OUTPUT,
     OPEN_INPUT,
     CLOSE_INPUT,
-    SET_STREAM_OUTPUT,
+    INVALIDATE_STREAM,
     SET_VOICE_VOLUME,
     GET_RENDER_POSITION,
     GET_INPUT_FRAMES_LOST,
@@ -545,13 +545,12 @@
         return reply.readInt32();
     }
 
-    virtual status_t setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output)
+    virtual status_t invalidateStream(audio_stream_type_t stream)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
         data.writeInt32((int32_t) stream);
-        data.writeInt32((int32_t) output);
-        remote()->transact(SET_STREAM_OUTPUT, data, &reply);
+        remote()->transact(INVALIDATE_STREAM, data, &reply);
         return reply.readInt32();
     }
 
@@ -1044,11 +1043,10 @@
             reply->writeInt32(closeInput((audio_io_handle_t) data.readInt32()));
             return NO_ERROR;
         } break;
-        case SET_STREAM_OUTPUT: {
+        case INVALIDATE_STREAM: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            uint32_t stream = data.readInt32();
-            audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
-            reply->writeInt32(setStreamOutput((audio_stream_type_t) stream, output));
+            audio_stream_type_t stream = (audio_stream_type_t) data.readInt32();
+            reply->writeInt32(invalidateStream(stream));
             return NO_ERROR;
         } break;
         case SET_VOICE_VOLUME: {
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index e914b34..f0f1832 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -90,7 +90,7 @@
             pLibConfig->sampleRate,
             AUDIO_FORMAT_PCM_16_BIT,
             audio_channel_out_mask_from_count(pLibConfig->numChannels),
-            mTrackBufferSize,
+            (size_t) mTrackBufferSize,
             AUDIO_OUTPUT_FLAG_NONE);
 
     // create render and playback thread
diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp
index 4885b4f..a55e09c 100644
--- a/media/libmedia/SoundPool.cpp
+++ b/media/libmedia/SoundPool.cpp
@@ -587,7 +587,7 @@
         uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5);
         uint32_t totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate;
         uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount;
-        uint32_t frameCount = 0;
+        size_t frameCount = 0;
 
         if (loop) {
             frameCount = sample->size()/numChannels/
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 8f21632..4189a5e 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -45,7 +45,6 @@
     libstagefright_rtsp         \
 
 LOCAL_C_INCLUDES :=                                                 \
-    $(call include-path-for, graphics corecg)                       \
     $(TOP)/frameworks/av/media/libstagefright/include               \
     $(TOP)/frameworks/av/media/libstagefright/rtsp                  \
     $(TOP)/frameworks/av/media/libstagefright/wifi-display          \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 142788d..200c561 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1455,7 +1455,7 @@
                 format, bufferCount, mSessionId, flags);
     uint32_t afSampleRate;
     size_t afFrameCount;
-    uint32_t frameCount;
+    size_t frameCount;
 
     // offloading is only supported in callback mode for now.
     // offloadInfo must be present if offload flag is set
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 845a589..5b7a236 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -748,7 +748,7 @@
     return OK;
 }
 
-status_t StagefrightRecorder::prepare() {
+status_t StagefrightRecorder::prepareInternal() {
     ALOGV("prepare");
     if (mOutputFd < 0) {
         ALOGE("Output file descriptor is invalid");
@@ -794,6 +794,13 @@
     return status;
 }
 
+status_t StagefrightRecorder::prepare() {
+    if (mVideoSource == VIDEO_SOURCE_SURFACE) {
+        return prepareInternal();
+    }
+    return OK;
+}
+
 status_t StagefrightRecorder::start() {
     ALOGV("start");
     if (mOutputFd < 0) {
@@ -801,15 +808,20 @@
         return INVALID_OPERATION;
     }
 
-    // Get UID here for permission checking
-    mClientUid = IPCThreadState::self()->getCallingUid();
+    status_t status = OK;
+
+    if (mVideoSource != VIDEO_SOURCE_SURFACE) {
+        status = prepareInternal();
+        if (status != OK) {
+            return status;
+        }
+    }
+
     if (mWriter == NULL) {
         ALOGE("File writer is not avaialble");
         return UNKNOWN_ERROR;
     }
 
-    status_t status = OK;
-
     switch (mOutputFormat) {
         case OUTPUT_FORMAT_DEFAULT:
         case OUTPUT_FORMAT_THREE_GPP:
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 7d6abd3..377d168 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -127,6 +127,7 @@
     sp<IGraphicBufferProducer> mGraphicBufferProducer;
     sp<ALooper> mLooper;
 
+    status_t prepareInternal();
     status_t setupMPEG4Recording();
     void setupMPEG4MetaData(sp<MetaData> *meta);
     status_t setupAMRRecording();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index d47ac98..a750ad0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1006,7 +1006,14 @@
                                     &NuPlayer::performScanSources));
                     }
 
-                    flushDecoder(audio, formatChange);
+                    sp<AMessage> newFormat = mSource->getFormat(audio);
+                    sp<Decoder> &decoder = audio ? mAudioDecoder : mVideoDecoder;
+                    if (formatChange && !decoder->supportsSeamlessFormatChange(newFormat)) {
+                        flushDecoder(audio, /* needShutdown = */ true);
+                    } else {
+                        flushDecoder(audio, /* needShutdown = */ false);
+                        err = OK;
+                    }
                 } else {
                     // This stream is unaffected by the discontinuity
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 22f699e..2423fd5 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -67,6 +67,7 @@
     // queue.
     bool needDedicatedLooper = !strncasecmp(mime.c_str(), "video/", 6);
 
+    mFormat = format;
     mCodec = new ACodec;
 
     if (needDedicatedLooper && mCodecLooper == NULL) {
@@ -147,5 +148,65 @@
     }
 }
 
+bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const {
+    if (targetFormat == NULL) {
+        return true;
+    }
+
+    AString mime;
+    if (!targetFormat->findString("mime", &mime)) {
+        return false;
+    }
+
+    if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
+        // field-by-field comparison
+        const char * keys[] = { "channel-count", "sample-rate", "is-adts" };
+        for (unsigned int i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {
+            int32_t oldVal, newVal;
+            if (!mFormat->findInt32(keys[i], &oldVal) || !targetFormat->findInt32(keys[i], &newVal)
+                    || oldVal != newVal) {
+                return false;
+            }
+        }
+
+        sp<ABuffer> oldBuf, newBuf;
+        if (mFormat->findBuffer("csd-0", &oldBuf) && targetFormat->findBuffer("csd-0", &newBuf)) {
+            if (oldBuf->size() != newBuf->size()) {
+                return false;
+            }
+            return !memcmp(oldBuf->data(), newBuf->data(), oldBuf->size());
+        }
+    }
+    return false;
+}
+
+bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetFormat) const {
+    if (mFormat == NULL) {
+        return false;
+    }
+
+    if (targetFormat == NULL) {
+        return true;
+    }
+
+    AString oldMime, newMime;
+    if (!mFormat->findString("mime", &oldMime)
+            || !targetFormat->findString("mime", &newMime)
+            || !(oldMime == newMime)) {
+        return false;
+    }
+
+    bool audio = !strncasecmp(oldMime.c_str(), "audio/", strlen("audio/"));
+    bool seamless;
+    if (audio) {
+        seamless = supportsSeamlessAudioFormatChange(targetFormat);
+    } else {
+        seamless = mCodec != NULL && mCodec->isConfiguredForAdaptivePlayback();
+    }
+
+    ALOGV("%s seamless support for %s", seamless ? "yes" : "no", oldMime.c_str());
+    return seamless;
+}
+
 }  // namespace android
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index a876148..78ea74a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -36,6 +36,8 @@
     void signalResume();
     void initiateShutdown();
 
+    bool supportsSeamlessFormatChange(const sp<AMessage> &to) const;
+
 protected:
     virtual ~Decoder();
 
@@ -49,6 +51,7 @@
     sp<AMessage> mNotify;
     sp<NativeWindowWrapper> mNativeWindow;
 
+    sp<AMessage> mFormat;
     sp<ACodec> mCodec;
     sp<ALooper> mCodecLooper;
 
@@ -59,6 +62,8 @@
 
     void onFillThisBuffer(const sp<AMessage> &msg);
 
+    bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const;
+
     DISALLOW_EVIL_CONSTRUCTORS(Decoder);
 };
 
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index df7da0a..d0e0e8e 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -65,7 +65,7 @@
     if (status == OK) {
         // make sure that the AudioRecord callback never returns more than the maximum
         // buffer size
-        int frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount;
+        uint32_t frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount;
 
         // make sure that the AudioRecord total buffer size is large enough
         size_t bufCount = 2;
@@ -76,10 +76,10 @@
         mRecord = new AudioRecord(
                     inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
                     audio_channel_in_mask_from_count(channelCount),
-                    bufCount * frameCount,
+                    (size_t) (bufCount * frameCount),
                     AudioRecordCallbackFunction,
                     this,
-                    frameCount);
+                    frameCount /*notificationFrames*/);
         mInitCheck = mRecord->initCheck();
     } else {
         mInitCheck = status;
diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c
index 15eabfb..52c85e5 100755
--- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c
+++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_intra_prediction.c
@@ -1110,7 +1110,7 @@
 
 /* Variables */
 
-    u32 i, j;
+    i32 i, j;
     i32 a, b, c;
     i32 tmp;
 
@@ -1123,20 +1123,20 @@
     a = 16 * (above[15] + left[15]);
 
     for (i = 0, b = 0; i < 8; i++)
-        b += ((i32)i + 1) * (above[8+i] - above[6-i]);
+        b += (i + 1) * (above[8+i] - above[6-i]);
     b = (5 * b + 32) >> 6;
 
     for (i = 0, c = 0; i < 7; i++)
-        c += ((i32)i + 1) * (left[8+i] - left[6-i]);
+        c += (i + 1) * (left[8+i] - left[6-i]);
     /* p[-1,-1] has to be accessed through above pointer */
-    c += ((i32)i + 1) * (left[8+i] - above[-1]);
+    c += (i + 1) * (left[8+i] - above[-1]);
     c = (5 * c + 32) >> 6;
 
     for (i = 0; i < 16; i++)
     {
         for (j = 0; j < 16; j++)
         {
-            tmp = (a + b * ((i32)j - 7) + c * ((i32)i - 7) + 16) >> 5;
+            tmp = (a + b * (j - 7) + c * (i - 7) + 16) >> 5;
             data[i*16+j] = (u8)CLIP1(tmp);
         }
     }
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index bc26de1..95779c4 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -1007,8 +1007,7 @@
         uint32_t resumeMask = 0;
 
         sp<AnotherPacketSource> sources[kMaxStreams];
-        // TRICKY: looping from i as earlier streams are already removed from streamMask
-        for (size_t j = i; j < kMaxStreams; ++j) {
+        for (size_t j = 0; j < kMaxStreams; ++j) {
             if ((streamMask & indexToType(j)) && uri == mStreams[j].mUri) {
                 sources[j] = mPacketSources.valueFor(indexToType(j));
                 resumeMask |= indexToType(j);
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 4dc5db0..587a6d5 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -24,6 +24,7 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
 #include <media/mediaplayer.h>
 
 namespace android {
@@ -352,9 +353,27 @@
     if (!meta->findString(key, &groupID)) {
         *uri = mItems.itemAt(index).mURI;
 
-        // Assume media without any more specific attribute contains
-        // audio and video, but no subtitles.
-        return !strcmp("audio", key) || !strcmp("video", key);
+        AString codecs;
+        if (!meta->findString("codecs", &codecs)) {
+            // Assume media without any more specific attribute contains
+            // audio and video, but no subtitles.
+            return !strcmp("audio", key) || !strcmp("video", key);
+        } else {
+            // Split the comma separated list of codecs.
+            size_t offset = 0;
+            ssize_t commaPos = -1;
+            codecs.append(',');
+            while ((commaPos = codecs.find(",", offset)) >= 0) {
+                AString codec(codecs, offset, commaPos - offset);
+                // return true only if a codec of type `key` ("audio"/"video")
+                // is found.
+                if (codecIsType(codec, key)) {
+                    return true;
+                }
+                offset = commaPos + 1;
+            }
+            return false;
+        }
     }
 
     sp<MediaGroup> group = mMediaGroups.valueFor(groupID);
@@ -682,12 +701,22 @@
                 *meta = new AMessage;
             }
             (*meta)->setInt32("bandwidth", x);
+        } else if (!strcasecmp("codecs", key.c_str())) {
+            if (!isQuotedString(val)) {
+                ALOGE("Expected quoted string for %s attribute, "
+                      "got '%s' instead.",
+                      key.c_str(), val.c_str());;
+
+                return ERROR_MALFORMED;
+            }
+
+            key.tolower();
+            const AString &codecs = unquoteString(val);
+            (*meta)->setString(key.c_str(), codecs.c_str());
         } else if (!strcasecmp("audio", key.c_str())
                 || !strcasecmp("video", key.c_str())
                 || !strcasecmp("subtitles", key.c_str())) {
-            if (val.size() < 2
-                    || val.c_str()[0] != '"'
-                    || val.c_str()[val.size() - 1] != '"') {
+            if (!isQuotedString(val)) {
                 ALOGE("Expected quoted string for %s attribute, "
                       "got '%s' instead.",
                       key.c_str(), val.c_str());
@@ -695,7 +724,7 @@
                 return ERROR_MALFORMED;
             }
 
-            AString groupID(val, 1, val.size() - 2);
+            const AString &groupID = unquoteString(val);
             ssize_t groupIndex = mMediaGroups.indexOfKey(groupID);
 
             if (groupIndex < 0) {
@@ -1084,4 +1113,121 @@
     return OK;
 }
 
+// static
+bool M3UParser::isQuotedString(const AString &str) {
+    if (str.size() < 2
+            || str.c_str()[0] != '"'
+            || str.c_str()[str.size() - 1] != '"') {
+        return false;
+    }
+    return true;
+}
+
+// static
+AString M3UParser::unquoteString(const AString &str) {
+     if (!isQuotedString(str)) {
+         return str;
+     }
+     return AString(str, 1, str.size() - 2);
+}
+
+// static
+bool M3UParser::codecIsType(const AString &codec, const char *type) {
+    if (codec.size() < 4) {
+        return false;
+    }
+    const char *c = codec.c_str();
+    switch (FOURCC(c[0], c[1], c[2], c[3])) {
+        // List extracted from http://www.mp4ra.org/codecs.html
+        case 'ac-3':
+        case 'alac':
+        case 'dra1':
+        case 'dtsc':
+        case 'dtse':
+        case 'dtsh':
+        case 'dtsl':
+        case 'ec-3':
+        case 'enca':
+        case 'g719':
+        case 'g726':
+        case 'm4ae':
+        case 'mlpa':
+        case 'mp4a':
+        case 'raw ':
+        case 'samr':
+        case 'sawb':
+        case 'sawp':
+        case 'sevc':
+        case 'sqcp':
+        case 'ssmv':
+        case 'twos':
+        case 'agsm':
+        case 'alaw':
+        case 'dvi ':
+        case 'fl32':
+        case 'fl64':
+        case 'ima4':
+        case 'in24':
+        case 'in32':
+        case 'lpcm':
+        case 'Qclp':
+        case 'QDM2':
+        case 'QDMC':
+        case 'ulaw':
+        case 'vdva':
+            return !strcmp("audio", type);
+
+        case 'avc1':
+        case 'avc2':
+        case 'avcp':
+        case 'drac':
+        case 'encv':
+        case 'mjp2':
+        case 'mp4v':
+        case 'mvc1':
+        case 'mvc2':
+        case 'resv':
+        case 's263':
+        case 'svc1':
+        case 'vc-1':
+        case 'CFHD':
+        case 'civd':
+        case 'DV10':
+        case 'dvh5':
+        case 'dvh6':
+        case 'dvhp':
+        case 'DVOO':
+        case 'DVOR':
+        case 'DVTV':
+        case 'DVVT':
+        case 'flic':
+        case 'gif ':
+        case 'h261':
+        case 'h263':
+        case 'HD10':
+        case 'jpeg':
+        case 'M105':
+        case 'mjpa':
+        case 'mjpb':
+        case 'png ':
+        case 'PNTG':
+        case 'rle ':
+        case 'rpza':
+        case 'Shr0':
+        case 'Shr1':
+        case 'Shr2':
+        case 'Shr3':
+        case 'Shr4':
+        case 'SVQ1':
+        case 'SVQ3':
+        case 'tga ':
+        case 'tiff':
+        case 'WRLE':
+            return !strcmp("video", type);
+
+        default:
+            return false;
+    }
+}
+
 }  // namespace android
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index 2051e41..ccd6556 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -96,6 +96,10 @@
     static status_t ParseInt32(const char *s, int32_t *x);
     static status_t ParseDouble(const char *s, double *x);
 
+    static bool isQuotedString(const AString &str);
+    static AString unquoteString(const AString &str);
+    static bool codecIsType(const AString &codec, const char *type);
+
     DISALLOW_EVIL_CONSTRUCTORS(M3UParser);
 };
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 7615086..92ee30e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -527,9 +527,24 @@
         goto Exit;
     }
 
+    // further sample rate checks are performed by createTrack_l() depending on the thread type
+    if (sampleRate == 0) {
+        ALOGE("createTrack() invalid sample rate %u", sampleRate);
+        lStatus = BAD_VALUE;
+        goto Exit;
+    }
+
+    // further channel mask checks are performed by createTrack_l() depending on the thread type
+    if (!audio_is_output_channel(channelMask)) {
+        ALOGE("createTrack() invalid channel mask %#x", channelMask);
+        lStatus = BAD_VALUE;
+        goto Exit;
+    }
+
     // client is responsible for conversion of 8-bit PCM to 16-bit PCM,
     // and we don't yet support 8.24 or 32-bit PCM
-    if (audio_is_linear_pcm(format) && format != AUDIO_FORMAT_PCM_16_BIT) {
+    if (!audio_is_valid_format(format) ||
+            (audio_is_linear_pcm(format) && format != AUDIO_FORMAT_PCM_16_BIT)) {
         ALOGE("createTrack() invalid format %#x", format);
         lStatus = BAD_VALUE;
         goto Exit;
@@ -1320,12 +1335,28 @@
         goto Exit;
     }
 
+    // further sample rate checks are performed by createRecordTrack_l()
+    if (sampleRate == 0) {
+        ALOGE("openRecord() invalid sample rate %u", sampleRate);
+        lStatus = BAD_VALUE;
+        goto Exit;
+    }
+
+    // FIXME when we support more formats, add audio_is_valid_format(format)
+    //       and any explicit restrictions if audio_is_linear_pcm(format)
     if (format != AUDIO_FORMAT_PCM_16_BIT) {
         ALOGE("openRecord() invalid format %#x", format);
         lStatus = BAD_VALUE;
         goto Exit;
     }
 
+    // further channel mask checks are performed by createRecordTrack_l()
+    if (!audio_is_input_channel(channelMask)) {
+        ALOGE("openRecord() invalid channel mask %#x", channelMask);
+        lStatus = BAD_VALUE;
+        goto Exit;
+    }
+
     // add client to list
     { // scope for mLock
         Mutex::Autolock _l(mLock);
@@ -1909,10 +1940,10 @@
     return NO_ERROR;
 }
 
-status_t AudioFlinger::setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output)
+status_t AudioFlinger::invalidateStream(audio_stream_type_t stream)
 {
     Mutex::Autolock _l(mLock);
-    ALOGV("setStreamOutput() stream %d to output %d", stream, output);
+    ALOGV("invalidateStream() stream %d", stream);
 
     for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
         PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 21d05d4..c2b516b 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -182,7 +182,7 @@
 
     virtual status_t closeInput(audio_io_handle_t input);
 
-    virtual status_t setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output);
+    virtual status_t invalidateStream(audio_stream_type_t stream);
 
     virtual status_t setVoiceVolume(float volume);
 
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 67d83b1..f00b82a 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -193,6 +193,7 @@
         t->mainBuffer = NULL;
         t->auxBuffer = NULL;
         t->downmixerBufferProvider = NULL;
+        t->mSinkFormat = AUDIO_FORMAT_PCM_16_BIT;
 
         status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask);
         if (status == OK) {
@@ -440,6 +441,13 @@
         //         for a specific track? or per mixer?
         /* case DOWNMIX_TYPE:
             break          */
+        case SINK_FORMAT: {
+            audio_format_t format = static_cast<audio_format_t>(valueInt);
+            if (track.mSinkFormat != format) {
+                track.mSinkFormat = format;
+                ALOGV("setParameter(TRACK, SINK_FORMAT, %#x)", format);
+            }
+            } break;
         default:
             LOG_FATAL("bad param");
         }
@@ -1043,7 +1051,7 @@
 void AudioMixer::process__nop(state_t* state, int64_t pts)
 {
     uint32_t e0 = state->enabledTracks;
-    size_t bufSize = state->frameCount * sizeof(int16_t) * MAX_NUM_CHANNELS;
+    size_t sampleCount = state->frameCount * MAX_NUM_CHANNELS;
     while (e0) {
         // process by group of tracks with same output buffer to
         // avoid multiple memset() on same buffer
@@ -1062,7 +1070,8 @@
             }
             e0 &= ~(e1);
 
-            memset(t1.mainBuffer, 0, bufSize);
+            memset(t1.mainBuffer, 0, sampleCount
+                    * audio_bytes_per_sample(t1.mSinkFormat));
         }
 
         while (e1) {
@@ -1170,8 +1179,18 @@
                     }
                 }
             }
-            ditherAndClamp(out, outTemp, BLOCKSIZE);
-            out += BLOCKSIZE;
+            switch (t1.mSinkFormat) {
+            case AUDIO_FORMAT_PCM_FLOAT:
+                memcpy_to_float_from_q19_12(reinterpret_cast<float *>(out), outTemp, BLOCKSIZE * 2);
+                out += BLOCKSIZE * 2; // output is 2 floats/frame.
+                break;
+            case AUDIO_FORMAT_PCM_16_BIT:
+                ditherAndClamp(out, outTemp, BLOCKSIZE);
+                out += BLOCKSIZE; // output is 1 int32_t (2 int16_t samples)/frame
+                break;
+            default:
+                LOG_ALWAYS_FATAL("bad sink format: %d", t1.mSinkFormat);
+            }
             numFrames += BLOCKSIZE;
         } while (numFrames < state->frameCount);
     }
@@ -1253,7 +1272,16 @@
                 }
             }
         }
-        ditherAndClamp(out, outTemp, numFrames);
+        switch (t1.mSinkFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            memcpy_to_float_from_q19_12(reinterpret_cast<float*>(out), outTemp, numFrames*2);
+            break;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            ditherAndClamp(out, outTemp, numFrames);
+            break;
+        default:
+            LOG_ALWAYS_FATAL("bad sink format: %d", t1.mSinkFormat);
+        }
     }
 }
 
@@ -1294,27 +1322,45 @@
         }
         size_t outFrames = b.frameCount;
 
-        if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
-            // volume is boosted, so we might need to clamp even though
-            // we process only one track.
+        switch (t.mSinkFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT: {
+            float *fout = reinterpret_cast<float*>(out);
+            static float scale = 1. / (32768. * 4096.); // exact when inverted
             do {
                 uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
                 in += 2;
-                int32_t l = mulRL(1, rl, vrl) >> 12;
-                int32_t r = mulRL(0, rl, vrl) >> 12;
-                // clamping...
-                l = clamp16(l);
-                r = clamp16(r);
-                *out++ = (r<<16) | (l & 0xFFFF);
+                int32_t l = mulRL(1, rl, vrl);
+                int32_t r = mulRL(0, rl, vrl);
+                *fout++ = static_cast<float>(l) * scale;
+                *fout++ = static_cast<float>(r) * scale;
             } while (--outFrames);
-        } else {
-            do {
-                uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
-                in += 2;
-                int32_t l = mulRL(1, rl, vrl) >> 12;
-                int32_t r = mulRL(0, rl, vrl) >> 12;
-                *out++ = (r<<16) | (l & 0xFFFF);
-            } while (--outFrames);
+            } break;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
+                // volume is boosted, so we might need to clamp even though
+                // we process only one track.
+                do {
+                    uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
+                    in += 2;
+                    int32_t l = mulRL(1, rl, vrl) >> 12;
+                    int32_t r = mulRL(0, rl, vrl) >> 12;
+                    // clamping...
+                    l = clamp16(l);
+                    r = clamp16(r);
+                    *out++ = (r<<16) | (l & 0xFFFF);
+                } while (--outFrames);
+            } else {
+                do {
+                    uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
+                    in += 2;
+                    int32_t l = mulRL(1, rl, vrl) >> 12;
+                    int32_t r = mulRL(0, rl, vrl) >> 12;
+                    *out++ = (r<<16) | (l & 0xFFFF);
+                } while (--outFrames);
+            }
+            break;
+        default:
+            LOG_ALWAYS_FATAL("bad sink format: %d", t.mSinkFormat);
         }
         numFrames -= b.frameCount;
         t.bufferProvider->releaseBuffer(&b);
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index d286986..3355db4 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -77,6 +77,7 @@
         MAIN_BUFFER     = 0x4002,
         AUX_BUFFER      = 0x4003,
         DOWNMIX_TYPE    = 0X4004,
+        SINK_FORMAT     = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
         // for target RESAMPLE
         SAMPLE_RATE     = 0x4100, // Configure sample rate conversion on this track name;
                                   // parameter 'value' is the new sample rate in Hz.
@@ -193,7 +194,9 @@
 
         int32_t     sessionId;
 
-        int32_t     padding[2];
+        audio_format_t mSinkFormat; // at this time: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+
+        int32_t     padding[1];
 
         // 16-byte boundary
 
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 9980344..41bd990 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -1598,15 +1598,14 @@
     return af->closeInput(input);
 }
 
-static int aps_set_stream_output(void *service __unused, audio_stream_type_t stream,
-                                     audio_io_handle_t output)
+static int aps_invalidate_stream(void *service __unused, audio_stream_type_t stream)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
     if (af == 0) {
         return PERMISSION_DENIED;
     }
 
-    return af->setStreamOutput(stream, output);
+    return af->invalidateStream(stream);
 }
 
 static int aps_move_effects(void *service __unused, int session,
@@ -1680,7 +1679,7 @@
         .open_input            = aps_open_input,
         .close_input           = aps_close_input,
         .set_stream_volume     = aps_set_stream_volume,
-        .set_stream_output     = aps_set_stream_output,
+        .invalidate_stream     = aps_invalidate_stream,
         .set_parameters        = aps_set_parameters,
         .get_parameters        = aps_get_parameters,
         .start_tone            = aps_start_tone,
diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp
index 939b128..7e4ca0c 100644
--- a/services/audioflinger/AudioResamplerDyn.cpp
+++ b/services/audioflinger/AudioResamplerDyn.cpp
@@ -165,6 +165,10 @@
     mCoefBuffer(NULL)
 {
     mVolumeSimd[0] = mVolumeSimd[1] = 0;
+    // The AudioResampler base class assumes we are always ready for 1:1 resampling.
+    // We reset mInSampleRate to 0, so setSampleRate() will calculate filters for
+    // setSampleRate() for 1:1. (May be removed if precalculated filters are used.)
+    mInSampleRate = 0;
     mConstants.set(128, 8, mSampleRate, mSampleRate); // TODO: set better
 }
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 3e8c133..be6fa19 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -104,10 +104,10 @@
 // maximum divider applied to the active sleep time in the mixer thread loop
 static const uint32_t kMaxThreadSleepTimeShift = 2;
 
-// minimum normal mix buffer size, expressed in milliseconds rather than frames
-static const uint32_t kMinNormalMixBufferSizeMs = 20;
-// maximum normal mix buffer size
-static const uint32_t kMaxNormalMixBufferSizeMs = 24;
+// minimum normal sink buffer size, expressed in milliseconds rather than frames
+static const uint32_t kMinNormalSinkBufferSizeMs = 20;
+// maximum normal sink buffer size
+static const uint32_t kMaxNormalSinkBufferSizeMs = 24;
 
 // Offloaded output thread standby delay: allows track transition without going to standby
 static const nsecs_t kOffloadStandbyDelayNs = seconds(1);
@@ -1066,7 +1066,7 @@
                                              audio_devices_t device,
                                              type_t type)
     :   ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type),
-        mNormalFrameCount(0), mMixBuffer(NULL),
+        mNormalFrameCount(0), mSinkBuffer(NULL),
         mSuspended(0), mBytesWritten(0),
         mActiveTracksGeneration(0),
         // mStreamTypes[] initialized in constructor body
@@ -1125,7 +1125,7 @@
 AudioFlinger::PlaybackThread::~PlaybackThread()
 {
     mAudioFlinger->unregisterWriter(mNBLogWriter);
-    delete[] mMixBuffer;
+    delete[] mSinkBuffer;
 }
 
 void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
@@ -1210,7 +1210,7 @@
     fdprintf(fd, "  Delayed writes: %d\n", mNumDelayedWrites);
     fdprintf(fd, "  Blocked in write: %s\n", mInWrite ? "yes" : "no");
     fdprintf(fd, "  Suspend count: %d\n", mSuspended);
-    fdprintf(fd, "  Mix buffer : %p\n", mMixBuffer);
+    fdprintf(fd, "  Sink buffer : %p\n", mSinkBuffer);
     fdprintf(fd, "  Fast track availMask=%#x\n", mFastTrackAvailMask);
 
     dumpBase(fd, args);
@@ -1716,12 +1716,12 @@
         }
     }
 
-    // Calculate size of normal mix buffer relative to the HAL output buffer size
+    // Calculate size of normal sink buffer relative to the HAL output buffer size
     double multiplier = 1.0;
     if (mType == MIXER && (kUseFastMixer == FastMixer_Static ||
             kUseFastMixer == FastMixer_Dynamic)) {
-        size_t minNormalFrameCount = (kMinNormalMixBufferSizeMs * mSampleRate) / 1000;
-        size_t maxNormalFrameCount = (kMaxNormalMixBufferSizeMs * mSampleRate) / 1000;
+        size_t minNormalFrameCount = (kMinNormalSinkBufferSizeMs * mSampleRate) / 1000;
+        size_t maxNormalFrameCount = (kMaxNormalSinkBufferSizeMs * mSampleRate) / 1000;
         // round up minimum and round down maximum to nearest 16 frames to satisfy AudioMixer
         minNormalFrameCount = (minNormalFrameCount + 15) & ~15;
         maxNormalFrameCount = maxNormalFrameCount & ~15;
@@ -1739,7 +1739,7 @@
             }
         } else {
             // prefer an even multiplier, for compatibility with doubling of fast tracks due to HAL
-            // SRC (it would be unusual for the normal mix buffer size to not be a multiple of fast
+            // SRC (it would be unusual for the normal sink buffer size to not be a multiple of fast
             // track, but we sometimes have to do this to satisfy the maximum frame count
             // constraint)
             // FIXME this rounding up should not be done if no HAL SRC
@@ -1755,14 +1755,14 @@
     mNormalFrameCount = multiplier * mFrameCount;
     // round up to nearest 16 frames to satisfy AudioMixer
     mNormalFrameCount = (mNormalFrameCount + 15) & ~15;
-    ALOGI("HAL output buffer size %u frames, normal mix buffer size %u frames", mFrameCount,
+    ALOGI("HAL output buffer size %u frames, normal sink buffer size %u frames", mFrameCount,
             mNormalFrameCount);
 
-    delete[] mMixBuffer;
+    delete[] mSinkBuffer;
     size_t normalBufferSize = mNormalFrameCount * mFrameSize;
-    // For historical reasons mMixBuffer is int16_t[], but mFrameSize can be odd (such as 1)
-    mMixBuffer = new int16_t[(normalBufferSize + 1) >> 1];
-    memset(mMixBuffer, 0, normalBufferSize);
+    // For historical reasons mSinkBuffer is int16_t[], but mFrameSize can be odd (such as 1)
+    mSinkBuffer = new int16_t[(normalBufferSize + 1) >> 1];
+    memset(mSinkBuffer, 0, normalBufferSize);
 
     // force reconfiguration of effect chains and engines to take new buffer size and audio
     // parameters into account
@@ -1958,7 +1958,7 @@
                         (pipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
             }
         }
-        ssize_t framesWritten = mNormalSink->write(mMixBuffer + offset, count);
+        ssize_t framesWritten = mNormalSink->write(mSinkBuffer + offset, count);
         ATRACE_END();
         if (framesWritten > 0) {
             bytesWritten = framesWritten << mBitShift;
@@ -1987,7 +1987,7 @@
         // FIXME We should have an implementation of timestamps for direct output threads.
         // They are used e.g for multichannel PCM playback over HDMI.
         bytesWritten = mOutput->stream->write(mOutput->stream,
-                                                   (char *)mMixBuffer + offset, mBytesRemaining);
+                                                   (char *)mSinkBuffer + offset, mBytesRemaining);
         if (mUseAsyncWrite &&
                 ((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) {
             // do not wait for async callback in case of error of full write
@@ -2026,7 +2026,7 @@
 
 /*
 The derived values that are cached:
- - mixBufferSize from frame count * frame size
+ - mSinkBufferSize from frame count * frame size
  - activeSleepTime from activeSleepTimeUs()
  - idleSleepTime from idleSleepTimeUs()
  - standbyDelay from mActiveSleepTimeUs (DIRECT only)
@@ -2045,7 +2045,7 @@
 
 void AudioFlinger::PlaybackThread::cacheParameters_l()
 {
-    mixBufferSize = mNormalFrameCount * mFrameSize;
+    mSinkBufferSize = mNormalFrameCount * mFrameSize;
     activeSleepTime = activeSleepTimeUs();
     idleSleepTime = idleSleepTimeUs();
 }
@@ -2068,13 +2068,13 @@
 status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
 {
     int session = chain->sessionId();
-    int16_t *buffer = mMixBuffer;
+    int16_t *buffer = mSinkBuffer;
     bool ownsBuffer = false;
 
     ALOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
     if (session > 0) {
         // Only one effect chain can be present in direct output thread and it uses
-        // the mix buffer as input
+        // the sink buffer as input
         if (mType != DIRECT) {
             size_t numSamples = mNormalFrameCount * mChannelCount;
             buffer = new int16_t[numSamples];
@@ -2108,7 +2108,7 @@
     }
 
     chain->setInBuffer(buffer, ownsBuffer);
-    chain->setOutBuffer(mMixBuffer);
+    chain->setOutBuffer(mSinkBuffer);
     // Effect chain for session AUDIO_SESSION_OUTPUT_STAGE is inserted at end of effect
     // chains list in order to be processed last as it contains output stage effects
     // Effect chain for session AUDIO_SESSION_OUTPUT_MIX is inserted before
@@ -2158,7 +2158,7 @@
             for (size_t i = 0; i < mTracks.size(); ++i) {
                 sp<Track> track = mTracks[i];
                 if (session == track->sessionId()) {
-                    track->setMainBuffer(mMixBuffer);
+                    track->setMainBuffer(mSinkBuffer);
                     chain->decTrackCnt();
                 }
             }
@@ -2361,14 +2361,14 @@
                 // must be written to HAL
                 threadLoop_sleepTime();
                 if (sleepTime == 0) {
-                    mCurrentWriteLength = mixBufferSize;
+                    mCurrentWriteLength = mSinkBufferSize;
                 }
             }
             mBytesRemaining = mCurrentWriteLength;
             if (isSuspended()) {
                 sleepTime = suspendSleepTimeUs();
                 // simulate write to HAL when suspended
-                mBytesWritten += mixBufferSize;
+                mBytesWritten += mSinkBufferSize;
                 mBytesRemaining = 0;
             }
 
@@ -2827,7 +2827,7 @@
 
     // mix buffers...
     mAudioMixer->process(pts);
-    mCurrentWriteLength = mixBufferSize;
+    mCurrentWriteLength = mSinkBufferSize;
     // increase sleep time progressively when application underrun condition clears.
     // Only increase sleep time if the mixer is ready for two consecutive times to avoid
     // that a steady state of alternating ready/not ready conditions keeps the sleep time
@@ -2861,7 +2861,7 @@
             sleepTime = idleSleepTime;
         }
     } else if (mBytesWritten != 0 || (mMixerStatus == MIXER_TRACKS_ENABLED)) {
-        memset(mMixBuffer, 0, mixBufferSize);
+        memset(mSinkBuffer, 0, mSinkBufferSize);
         sleepTime = 0;
         ALOGV_IF(mBytesWritten == 0 && (mMixerStatus == MIXER_TRACKS_ENABLED),
                 "anticipated start");
@@ -3109,10 +3109,10 @@
 
             mixedTracks++;
 
-            // track->mainBuffer() != mMixBuffer means there is an effect chain
+            // track->mainBuffer() != mSinkBuffer means there is an effect chain
             // connected to the track
             chain.clear();
-            if (track->mainBuffer() != mMixBuffer) {
+            if (track->mainBuffer() != mSinkBuffer) {
                 chain = getEffectChain_l(track->sessionId());
                 // Delegate volume control to effect in track effect chain if needed
                 if (chain != 0) {
@@ -3355,13 +3355,13 @@
     // remove all the tracks that need to be...
     removeTracks_l(*tracksToRemove);
 
-    // mix buffer must be cleared if all tracks are connected to an
+    // sink buffer must be cleared if all tracks are connected to an
     // effect chain as in this case the mixer will not write to
-    // mix buffer and track effects will accumulate into it
+    // sink buffer and track effects will accumulate into it
     if ((mBytesRemaining == 0) && ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
             (mixedTracks == 0 && fastTracks > 0))) {
         // FIXME as a performance optimization, should remember previous zero status
-        memset(mMixBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t));
+        memset(mSinkBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t));
     }
 
     // if any fast tracks, then status is ready
@@ -3749,7 +3749,7 @@
 void AudioFlinger::DirectOutputThread::threadLoop_mix()
 {
     size_t frameCount = mFrameCount;
-    int8_t *curBuf = (int8_t *)mMixBuffer;
+    int8_t *curBuf = (int8_t *)mSinkBuffer;
     // output audio to hardware
     while (frameCount) {
         AudioBufferProvider::Buffer buffer;
@@ -3764,7 +3764,7 @@
         curBuf += buffer.frameCount * mFrameSize;
         mActiveTrack->releaseBuffer(&buffer);
     }
-    mCurrentWriteLength = curBuf - (int8_t *)mMixBuffer;
+    mCurrentWriteLength = curBuf - (int8_t *)mSinkBuffer;
     sleepTime = 0;
     standbyTime = systemTime() + standbyDelay;
     mActiveTrack.clear();
@@ -3779,7 +3779,7 @@
             sleepTime = idleSleepTime;
         }
     } else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) {
-        memset(mMixBuffer, 0, mFrameCount * mFrameSize);
+        memset(mSinkBuffer, 0, mFrameCount * mFrameSize);
         sleepTime = 0;
     }
 }
@@ -4306,11 +4306,11 @@
     if (outputsReady(outputTracks)) {
         mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
     } else {
-        memset(mMixBuffer, 0, mixBufferSize);
+        memset(mSinkBuffer, 0, mSinkBufferSize);
     }
     sleepTime = 0;
     writeFrames = mNormalFrameCount;
-    mCurrentWriteLength = mixBufferSize;
+    mCurrentWriteLength = mSinkBufferSize;
     standbyTime = systemTime() + standbyDelay;
 }
 
@@ -4325,7 +4325,7 @@
     } else if (mBytesWritten != 0) {
         if (mMixerStatus == MIXER_TRACKS_ENABLED) {
             writeFrames = mNormalFrameCount;
-            memset(mMixBuffer, 0, mixBufferSize);
+            memset(mSinkBuffer, 0, mSinkBufferSize);
         } else {
             // flush remaining overflow buffers in output tracks
             writeFrames = 0;
@@ -4337,10 +4337,10 @@
 ssize_t AudioFlinger::DuplicatingThread::threadLoop_write()
 {
     for (size_t i = 0; i < outputTracks.size(); i++) {
-        outputTracks[i]->write(mMixBuffer, writeFrames);
+        outputTracks[i]->write(mSinkBuffer, writeFrames);
     }
     mStandby = false;
-    return (ssize_t)mixBufferSize;
+    return (ssize_t)mSinkBufferSize;
 }
 
 void AudioFlinger::DuplicatingThread::threadLoop_standby()
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index fa3563c..b276ab2 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -450,7 +450,8 @@
     virtual     String8     getParameters(const String8& keys);
     virtual     void        audioConfigChanged_l(int event, int param = 0);
                 status_t    getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
-                int16_t     *mixBuffer() const { return mMixBuffer; };
+                // TODO: rename mixBuffer() to sinkBuffer() or try to remove external use.
+                int16_t     *mixBuffer() const { return mSinkBuffer; };
 
     virtual     void detachAuxEffect_l(int effectId);
                 status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track,
@@ -481,7 +482,7 @@
     // updated by readOutputParameters_l()
     size_t                          mNormalFrameCount;  // normal mixer and effects
 
-    int16_t*                        mMixBuffer;         // frame size aligned mix buffer
+    int16_t*                        mSinkBuffer;         // frame size aligned sink buffer
 
     // suspend count, > 0 means suspended.  While suspended, the thread continues to pull from
     // tracks and mix, but doesn't write to HAL.  A2DP and SCO HAL implementations can't handle
@@ -560,7 +561,7 @@
 
     // FIXME rename these former local variables of threadLoop to standard "m" names
     nsecs_t                         standbyTime;
-    size_t                          mixBufferSize;
+    size_t                          mSinkBufferSize;
 
     // cached copies of activeSleepTimeUs() and idleSleepTimeUs() made by cacheParameters_l()
     uint32_t                        activeSleepTime;
diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp
index 74d5702..3ab3ba9 100644
--- a/services/audioflinger/test-resample.cpp
+++ b/services/audioflinger/test-resample.cpp
@@ -27,6 +27,7 @@
 #include <time.h>
 #include <math.h>
 #include <audio_utils/sndfile.h>
+#include <utils/Vector.h>
 
 using namespace android;
 
@@ -34,7 +35,7 @@
 
 static int usage(const char* name) {
     fprintf(stderr,"Usage: %s [-p] [-h] [-v] [-s] [-q {dq|lq|mq|hq|vhq|dlq|dmq|dhq}]"
-                   " [-i input-sample-rate] [-o output-sample-rate] [-O #] [<input-file>]"
+                   " [-i input-sample-rate] [-o output-sample-rate] [-O csv] [-P csv] [<input-file>]"
                    " <output-file>\n", name);
     fprintf(stderr,"    -p    enable profiling\n");
     fprintf(stderr,"    -h    create wav file\n");
@@ -51,10 +52,50 @@
     fprintf(stderr,"              dhq : dynamic high quality\n");
     fprintf(stderr,"    -i    input file sample rate (ignored if input file is specified)\n");
     fprintf(stderr,"    -o    output file sample rate\n");
-    fprintf(stderr,"    -O    # frames output per call to resample()\n");
+    fprintf(stderr,"    -O    # frames output per call to resample() in CSV format\n");
+    fprintf(stderr,"    -P    # frames provided per call to resample() in CSV format\n");
     return -1;
 }
 
+// Convert a list of integers in CSV format to a Vector of those values.
+// Returns the number of elements in the list, or -1 on error.
+int parseCSV(const char *string, Vector<int>& values)
+{
+    // pass 1: count the number of values and do syntax check
+    size_t numValues = 0;
+    bool hadDigit = false;
+    for (const char *p = string; ; ) {
+        switch (*p++) {
+        case '0': case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8': case '9':
+            hadDigit = true;
+            break;
+        case '\0':
+            if (hadDigit) {
+                // pass 2: allocate and initialize vector of values
+                values.resize(++numValues);
+                values.editItemAt(0) = atoi(p = optarg);
+                for (size_t i = 1; i < numValues; ) {
+                    if (*p++ == ',') {
+                        values.editItemAt(i++) = atoi(p);
+                    }
+                }
+                return numValues;
+            }
+            // fall through
+        case ',':
+            if (hadDigit) {
+                hadDigit = false;
+                numValues++;
+                break;
+            }
+            // fall through
+        default:
+            return -1;
+        }
+    }
+}
+
 int main(int argc, char* argv[]) {
 
     const char* const progname = argv[0];
@@ -65,10 +106,11 @@
     int input_freq = 0;
     int output_freq = 0;
     AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY;
-    size_t framesPerCall = 0;
+    Vector<int> Ovalues;
+    Vector<int> Pvalues;
 
     int ch;
-    while ((ch = getopt(argc, argv, "pfhvsq:i:o:O:")) != -1) {
+    while ((ch = getopt(argc, argv, "pfhvsq:i:o:O:P:")) != -1) {
         switch (ch) {
         case 'p':
             profileResample = true;
@@ -114,7 +156,16 @@
             output_freq = atoi(optarg);
             break;
         case 'O':
-            framesPerCall = atoi(optarg);
+            if (parseCSV(optarg, Ovalues) < 0) {
+                fprintf(stderr, "incorrect syntax for -O option\n");
+                return -1;
+            }
+            break;
+        case 'P':
+            if (parseCSV(optarg, Pvalues) < 0) {
+                fprintf(stderr, "incorrect syntax for -P option\n");
+                return -1;
+            }
             break;
         case '?':
         default:
@@ -182,12 +233,14 @@
         const int       mChannels;
         size_t          mNextFrame; // index of next frame to provide
         size_t          mUnrel;     // number of frames not yet released
+        const Vector<int> mPvalues; // number of frames provided per call
+        size_t          mNextPidx;  // index of next entry in mPvalues to use
     public:
-        Provider(const void* addr, size_t size, int channels)
+        Provider(const void* addr, size_t size, int channels, const Vector<int>& Pvalues)
           : mAddr((int16_t*) addr),
             mNumFrames(size / (channels*sizeof(int16_t))),
             mChannels(channels),
-            mNextFrame(0), mUnrel(0) {
+            mNextFrame(0), mUnrel(0), mPvalues(Pvalues), mNextPidx(0) {
         }
         virtual status_t getNextBuffer(Buffer* buffer,
                 int64_t pts = kInvalidPTS) {
@@ -196,6 +249,16 @@
             if (requestedFrames > mNumFrames - mNextFrame) {
                 buffer->frameCount = mNumFrames - mNextFrame;
             }
+            if (!mPvalues.isEmpty()) {
+                size_t provided = mPvalues[mNextPidx++];
+                printf("mPvalue[%d]=%u not %u\n", mNextPidx-1, provided, buffer->frameCount);
+                if (provided < buffer->frameCount) {
+                    buffer->frameCount = provided;
+                }
+                if (mNextPidx >= mPvalues.size()) {
+                    mNextPidx = 0;
+                }
+            }
             if (gVerbose) {
                 printf("getNextBuffer() requested %u frames out of %u frames available,"
                         " and returned %u frames\n",
@@ -230,7 +293,7 @@
         void reset() {
             mNextFrame = 0;
         }
-    } provider(input_vaddr, input_size, channels);
+    } provider(input_vaddr, input_size, channels, Pvalues);
 
     size_t input_frames = input_size / (channels * sizeof(int16_t));
     if (gVerbose) {
@@ -348,11 +411,17 @@
     if (gVerbose) {
         printf("resample() %u output frames\n", out_frames);
     }
-    if (framesPerCall == 0 || framesPerCall > out_frames) {
-        framesPerCall = out_frames;
+    if (Ovalues.isEmpty()) {
+        Ovalues.push(out_frames);
     }
-    for (size_t i = 0; i < out_frames; ) {
-        size_t thisFrames = framesPerCall <= out_frames - i ? framesPerCall : out_frames - i;
+    for (size_t i = 0, j = 0; i < out_frames; ) {
+        size_t thisFrames = Ovalues[j++];
+        if (j >= Ovalues.size()) {
+            j = 0;
+        }
+        if (thisFrames == 0 || thisFrames > out_frames - i) {
+            thisFrames = out_frames - i;
+        }
         resampler->resample((int*) output_vaddr + 2*i, thisFrames, &provider);
         i += thisFrames;
     }
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 0a88a75..80b7cd4 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -407,12 +407,6 @@
         l.mParameters.state = Parameters::DISCONNECTED;
     }
 
-    mStreamingProcessor->deletePreviewStream();
-    mStreamingProcessor->deleteRecordingStream();
-    mJpegProcessor->deleteStream();
-    mCallbackProcessor->deleteStream();
-    mZslProcessor->deleteStream();
-
     mStreamingProcessor->requestExit();
     mFrameProcessor->requestExit();
     mCaptureSequencer->requestExit();
@@ -429,6 +423,14 @@
     mZslProcessorThread->join();
     mCallbackProcessor->join();
 
+    ALOGV("Camera %d: Deleting streams", mCameraId);
+
+    mStreamingProcessor->deletePreviewStream();
+    mStreamingProcessor->deleteRecordingStream();
+    mJpegProcessor->deleteStream();
+    mCallbackProcessor->deleteStream();
+    mZslProcessor->deleteStream();
+
     ALOGV("Camera %d: Disconnecting device", mCameraId);
 
     mDevice->disconnect();