Camera2: Refactor preview/recording management to separate class.

Untangle preview/recording management from main class to simplify
callpaths; in preparation for fixing several HAL/app interactions
bugs.

Bug: 7172543
Bug: 7159577
Bug: 7107220
Change-Id: Iab5503f66b35d88a1524111536a484c9e33fd934
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index c7927fe..eff47c8 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -14,6 +14,7 @@
     camera2/CameraMetadata.cpp \
     camera2/Parameters.cpp \
     camera2/FrameProcessor.cpp \
+    camera2/StreamingProcessor.cpp \
     camera2/JpegProcessor.cpp \
     camera2/CallbackProcessor.cpp \
     camera2/ZslProcessor.cpp \
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index d315abb..33e0b56 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -24,7 +24,6 @@
 #include <cutils/properties.h>
 #include <gui/SurfaceTextureClient.h>
 #include <gui/Surface.h>
-#include <media/hardware/MetadataBufferType.h>
 #include "camera2/Parameters.h"
 #include "Camera2Client.h"
 
@@ -52,10 +51,7 @@
         Client(cameraService, cameraClient,
                 cameraId, cameraFacing, clientPid),
         mSharedCameraClient(cameraClient),
-        mParameters(cameraId, cameraFacing),
-        mPreviewStreamId(NO_STREAM),
-        mRecordingStreamId(NO_STREAM),
-        mRecordingHeapCount(kDefaultRecordingHeapCount)
+        mParameters(cameraId, cameraFacing)
 {
     ATRACE_CALL();
     ALOGI("Camera %d: Opened", cameraId);
@@ -101,6 +97,8 @@
 
     String8 threadName;
 
+    mStreamingProcessor = new StreamingProcessor(this);
+
     mFrameProcessor = new FrameProcessor(this);
     threadName = String8::format("C2-%d-FrameProc",
             mCameraId);
@@ -140,9 +138,12 @@
 
     mDestructionStarted = true;
 
-    // Rewrite mClientPid to allow shutdown by CameraService
-    mClientPid = getCallingPid();
-    disconnect();
+    SharedParameters::Lock l(mParameters);
+    if (l.mParameters.state != Parameters::DISCONNECTED) {
+        // Rewrite mClientPid to allow shutdown by CameraService
+        mClientPid = getCallingPid();
+        disconnect();
+    }
 
     ALOGI("Camera %d: Closed", mCameraId);
 }
@@ -314,25 +315,9 @@
             getCaptureStreamId());
     result.appendFormat("    Recording stream ID: %d\n",
             getRecordingStreamId());
+    write(fd, result.string(), result.size());
 
-    result.append("  Current requests:\n");
-    if (mPreviewRequest.entryCount() != 0) {
-        result.append("    Preview request:\n");
-        write(fd, result.string(), result.size());
-        mPreviewRequest.dump(fd, 2, 6);
-    } else {
-        result.append("    Preview request: undefined\n");
-        write(fd, result.string(), result.size());
-    }
-
-    if (mRecordingRequest.entryCount() != 0) {
-        result = "    Recording request:\n";
-        write(fd, result.string(), result.size());
-        mRecordingRequest.dump(fd, 2, 6);
-    } else {
-        result = "    Recording request: undefined\n";
-        write(fd, result.string(), result.size());
-    }
+    mStreamingProcessor->dump(fd, args);
 
     mCaptureSequencer->dump(fd, args);
 
@@ -373,20 +358,10 @@
         l.mParameters.state = Parameters::DISCONNECTED;
     }
 
-    if (mPreviewStreamId != NO_STREAM) {
-        mDevice->deleteStream(mPreviewStreamId);
-        mPreviewStreamId = NO_STREAM;
-    }
-
+    mStreamingProcessor->deletePreviewStream();
+    mStreamingProcessor->deleteRecordingStream();
     mJpegProcessor->deleteStream();
-
-    if (mRecordingStreamId != NO_STREAM) {
-        mDevice->deleteStream(mRecordingStreamId);
-        mRecordingStreamId = NO_STREAM;
-    }
-
     mCallbackProcessor->deleteStream();
-
     mZslProcessor->deleteStream();
 
     mFrameProcessor->requestExit();
@@ -546,24 +521,13 @@
             break;
     }
 
-    if (mPreviewStreamId != NO_STREAM) {
-        res = mDevice->waitUntilDrained();
-        if (res != OK) {
-            ALOGE("%s: Error waiting for preview to drain: %s (%d)",
-                    __FUNCTION__, strerror(-res), res);
-            return res;
-        }
-        res = mDevice->deleteStream(mPreviewStreamId);
-        if (res != OK) {
-            ALOGE("%s: Unable to delete old preview stream: %s (%d)",
-                    __FUNCTION__, strerror(-res), res);
-            return res;
-        }
-        mPreviewStreamId = NO_STREAM;
-    }
-
     mPreviewSurface = binder;
-    mPreviewWindow = window;
+    res = mStreamingProcessor->setPreviewWindow(window);
+    if (res != OK) {
+        ALOGE("%s: Unable to set new preview window: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        return res;
+    }
 
     if (l.mParameters.state == Parameters::WAITING_FOR_PREVIEW_WINDOW) {
         return startPreviewL(l.mParameters, false);
@@ -637,18 +601,20 @@
         return INVALID_OPERATION;
     }
 
-    if (mPreviewWindow == 0) {
+    if (!mStreamingProcessor->haveValidPreviewWindow()) {
         params.state = Parameters::WAITING_FOR_PREVIEW_WINDOW;
         return OK;
     }
     params.state = Parameters::STOPPED;
 
-    res = updatePreviewStream(params);
+    res = mStreamingProcessor->updatePreviewStream(params);
     if (res != OK) {
         ALOGE("%s: Camera %d: Unable to update preview stream: %s (%d)",
                 __FUNCTION__, mCameraId, strerror(-res), res);
         return res;
     }
+
+    Vector<uint8_t> outputStreams;
     bool callbacksEnabled = params.previewCallbackFlags &
         CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK;
     if (callbacksEnabled) {
@@ -658,6 +624,7 @@
                     __FUNCTION__, mCameraId, strerror(-res), res);
             return res;
         }
+        outputStreams.push(getCallbackStreamId());
     }
     if (params.zslMode && !params.recordingHint) {
         res = mZslProcessor->updateStream(params);
@@ -666,38 +633,28 @@
                     __FUNCTION__, mCameraId, strerror(-res), res);
             return res;
         }
+        outputStreams.push(getZslStreamId());
     }
 
-    CameraMetadata *request;
+    outputStreams.push(getPreviewStreamId());
+
     if (!params.recordingHint) {
-        if (mPreviewRequest.entryCount() == 0) {
-            res = updatePreviewRequest(params);
+        if (!restart) {
+            res = mStreamingProcessor->updatePreviewRequest(params);
             if (res != OK) {
-                ALOGE("%s: Camera %d: Unable to create preview request: %s (%d)",
-                        __FUNCTION__, mCameraId, strerror(-res), res);
+                ALOGE("%s: Camera %d: Can't set up preview request: "
+                        "%s (%d)", __FUNCTION__, mCameraId,
+                        strerror(-res), res);
                 return res;
             }
         }
-        request = &mPreviewRequest;
+        res = mStreamingProcessor->startStream(StreamingProcessor::PREVIEW,
+                outputStreams);
     } else {
         // With recording hint set, we're going to be operating under the
         // assumption that the user will record video. To optimize recording
         // startup time, create the necessary output streams for recording and
         // video snapshot now if they don't already exist.
-        if (mRecordingRequest.entryCount() == 0) {
-            res = updateRecordingRequest(params);
-            if (res != OK) {
-                ALOGE("%s: Camera %d: Unable to create recording preview "
-                        "request: %s (%d)",
-                        __FUNCTION__, mCameraId, strerror(-res), res);
-                return res;
-            }
-        }
-        request = &mRecordingRequest;
-
-        // TODO: Re-enable recording stream creation/update here once issues are
-        // resolved
-
         res = mJpegProcessor->updateStream(params);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't pre-configure still image "
@@ -705,43 +662,26 @@
                     __FUNCTION__, mCameraId, strerror(-res), res);
             return res;
         }
+
+        if (!restart) {
+            res = mStreamingProcessor->updateRecordingRequest(params);
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Can't set up preview request with "
+                        "record hint: %s (%d)", __FUNCTION__, mCameraId,
+                        strerror(-res), res);
+                return res;
+            }
+        }
+        res = mStreamingProcessor->startStream(StreamingProcessor::RECORD,
+                outputStreams);
     }
-
-    Vector<uint8_t> outputStreams;
-    outputStreams.push(getPreviewStreamId());
-
-    if (callbacksEnabled) {
-        outputStreams.push(getCallbackStreamId());
-    }
-    if (params.zslMode && !params.recordingHint) {
-        outputStreams.push(getZslStreamId());
-    }
-
-    res = request->update(
-        ANDROID_REQUEST_OUTPUT_STREAMS,
-        outputStreams);
-
     if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to set up preview request: %s (%d)",
-                __FUNCTION__, mCameraId, strerror(-res), res);
-        return res;
-    }
-    res = request->sort();
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Error sorting preview request: %s (%d)",
+        ALOGE("%s: Camera %d: Unable to start streaming preview: %s (%d)",
                 __FUNCTION__, mCameraId, strerror(-res), res);
         return res;
     }
 
-    res = mDevice->setStreamingRequest(*request);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to set preview request to start preview: "
-                "%s (%d)",
-                __FUNCTION__, mCameraId, strerror(-res), res);
-        return res;
-    }
     params.state = Parameters::PREVIEW;
-
     return OK;
 }
 
@@ -776,8 +716,7 @@
         case Parameters::RECORD:
             // no break - identical to preview
         case Parameters::PREVIEW:
-            mDevice->clearStreamingRequest();
-            mDevice->waitUntilDrained();
+            mStreamingProcessor->stopStream();
             // no break
         case Parameters::WAITING_FOR_PREVIEW_WINDOW: {
             SharedParameters::Lock l(mParameters);
@@ -866,14 +805,24 @@
         return INVALID_OPERATION;
     }
 
-    mCameraService->playSound(CameraService::SOUND_RECORDING);
+    if (!restart) {
+        mCameraService->playSound(CameraService::SOUND_RECORDING);
+        mStreamingProcessor->updateRecordingRequest(params);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Unable to update recording request: %s (%d)",
+                    __FUNCTION__, mCameraId, strerror(-res), res);
+            return res;
+        }
+    }
 
-    res = updateRecordingStream(params);
+    res = mStreamingProcessor->updateRecordingStream(params);
     if (res != OK) {
         ALOGE("%s: Camera %d: Unable to update recording stream: %s (%d)",
                 __FUNCTION__, mCameraId, strerror(-res), res);
         return res;
     }
+
+    Vector<uint8_t> outputStreams;
     bool callbacksEnabled = params.previewCallbackFlags &
         CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK;
     if (callbacksEnabled) {
@@ -883,54 +832,19 @@
                     __FUNCTION__, mCameraId, strerror(-res), res);
             return res;
         }
+        outputStreams.push(getCallbackStreamId());
     }
+    outputStreams.push(getPreviewStreamId());
+    outputStreams.push(getRecordingStreamId());
 
-    if (mRecordingRequest.entryCount() == 0) {
-        res = updateRecordingRequest(params);
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Unable to create recording request: %s (%d)",
-                    __FUNCTION__, mCameraId, strerror(-res), res);
-            return res;
-        }
-    }
-
-    if (callbacksEnabled) {
-        uint8_t outputStreams[3] ={
-            getPreviewStreamId(),
-            getRecordingStreamId(),
-            getCallbackStreamId()
-        };
-        res = mRecordingRequest.update(
-                ANDROID_REQUEST_OUTPUT_STREAMS,
-                outputStreams, 3);
-    } else {
-        uint8_t outputStreams[2] = {
-            getPreviewStreamId(),
-            getRecordingStreamId()
-        };
-        res = mRecordingRequest.update(
-                ANDROID_REQUEST_OUTPUT_STREAMS,
-                outputStreams, 2);
-    }
+    res = mStreamingProcessor->startStream(StreamingProcessor::RECORD,
+            outputStreams);
     if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to set up recording request: %s (%d)",
-                __FUNCTION__, mCameraId, strerror(-res), res);
-        return res;
-    }
-    res = mRecordingRequest.sort();
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Error sorting recording request: %s (%d)",
+        ALOGE("%s: Camera %d: Unable to start recording stream: %s (%d)",
                 __FUNCTION__, mCameraId, strerror(-res), res);
         return res;
     }
 
-    res = mDevice->setStreamingRequest(mRecordingRequest);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to set recording request to start "
-                "recording: %s (%d)", __FUNCTION__, mCameraId,
-                strerror(-res), res);
-        return res;
-    }
     if (params.state < Parameters::RECORD) {
         params.state = Parameters::RECORD;
     }
@@ -991,60 +905,9 @@
 void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) {
     ATRACE_CALL();
     Mutex::Autolock icl(mICameraLock);
-    status_t res;
     if ( checkPid(__FUNCTION__) != OK) return;
 
-    SharedParameters::Lock l(mParameters);
-
-    // Make sure this is for the current heap
-    ssize_t offset;
-    size_t size;
-    sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
-    if (heap->getHeapID() != mRecordingHeap->mHeap->getHeapID()) {
-        ALOGW("%s: Camera %d: Mismatched heap ID, ignoring release "
-                "(got %x, expected %x)", __FUNCTION__, mCameraId,
-                heap->getHeapID(), mRecordingHeap->mHeap->getHeapID());
-        return;
-    }
-    uint8_t *data = (uint8_t*)heap->getBase() + offset;
-    uint32_t type = *(uint32_t*)data;
-    if (type != kMetadataBufferTypeGrallocSource) {
-        ALOGE("%s: Camera %d: Recording frame type invalid (got %x, expected %x)",
-                __FUNCTION__, mCameraId, type, kMetadataBufferTypeGrallocSource);
-        return;
-    }
-
-    // Release the buffer back to the recording queue
-
-    buffer_handle_t imgHandle = *(buffer_handle_t*)(data + 4);
-
-    size_t itemIndex;
-    for (itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) {
-        const BufferItemConsumer::BufferItem item = mRecordingBuffers[itemIndex];
-        if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT &&
-                item.mGraphicBuffer->handle == imgHandle) {
-            break;
-        }
-    }
-    if (itemIndex == mRecordingBuffers.size()) {
-        ALOGE("%s: Camera %d: Can't find buffer_handle_t %p in list of "
-                "outstanding buffers", __FUNCTION__, mCameraId, imgHandle);
-        return;
-    }
-
-    ALOGV("%s: Camera %d: Freeing buffer_handle_t %p", __FUNCTION__, mCameraId,
-            imgHandle);
-
-    res = mRecordingConsumer->releaseBuffer(mRecordingBuffers[itemIndex]);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to free recording frame (buffer_handle_t: %p):"
-                "%s (%d)",
-                __FUNCTION__, mCameraId, imgHandle, strerror(-res), res);
-        return;
-    }
-    mRecordingBuffers.replaceAt(itemIndex);
-
-    mRecordingHeapFree++;
+    mStreamingProcessor->releaseRecordingFrame(mem);
 }
 
 status_t Camera2Client::autoFocus() {
@@ -1222,8 +1085,8 @@
     }
     SharedParameters::Lock l(mParameters);
     if (transform != l.mParameters.previewTransform &&
-            mPreviewStreamId != NO_STREAM) {
-        mDevice->setStreamTransform(mPreviewStreamId, transform);
+            getPreviewStreamId() != NO_STREAM) {
+        mDevice->setStreamTransform(getPreviewStreamId(), transform);
     }
     l.mParameters.previewTransform = transform;
     return OK;
@@ -1336,23 +1199,7 @@
         return INVALID_OPERATION;
     }
 
-    // 32 is the current upper limit on the video buffer count for BufferQueue
-    if (count > 32) {
-        ALOGE("%s: Camera %d: Error setting %d as video buffer count value",
-                __FUNCTION__, mCameraId, count);
-        return BAD_VALUE;
-    }
-
-    // Need to reallocate memory for heap
-    if (mRecordingHeapCount != count) {
-        if  (mRecordingHeap != 0) {
-            mRecordingHeap.clear();
-            mRecordingHeap = NULL;
-        }
-        mRecordingHeapCount = count;
-    }
-
-    return OK;
+    return mStreamingProcessor->setRecordingBufferCount(count);
 }
 
 /** Device-related methods */
@@ -1503,7 +1350,7 @@
 }
 
 int Camera2Client::getPreviewStreamId() const {
-    return mPreviewStreamId;
+    return mStreamingProcessor->getPreviewStreamId();
 }
 
 int Camera2Client::getCaptureStreamId() const {
@@ -1515,7 +1362,7 @@
 }
 
 int Camera2Client::getRecordingStreamId() const {
-    return mRecordingStreamId;
+    return mStreamingProcessor->getRecordingStreamId();
 }
 
 int Camera2Client::getZslStreamId() const {
@@ -1561,117 +1408,18 @@
 const int32_t Camera2Client::kRecordRequestId;
 const int32_t Camera2Client::kFirstCaptureRequestId;
 
-void Camera2Client::onRecordingFrameAvailable() {
-    ATRACE_CALL();
-    status_t res;
-    sp<Camera2Heap> recordingHeap;
-    size_t heapIdx = 0;
-    nsecs_t timestamp;
-    {
-        SharedParameters::Lock l(mParameters);
-
-        BufferItemConsumer::BufferItem imgBuffer;
-        res = mRecordingConsumer->acquireBuffer(&imgBuffer);
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Error receiving recording buffer: %s (%d)",
-                    __FUNCTION__, mCameraId, strerror(-res), res);
-            return;
-        }
-        timestamp = imgBuffer.mTimestamp;
-
-        mRecordingFrameCount++;
-        ALOGV("OnRecordingFrame: Frame %d", mRecordingFrameCount);
-
-        // TODO: Signal errors here upstream
-        if (l.mParameters.state != Parameters::RECORD &&
-                l.mParameters.state != Parameters::VIDEO_SNAPSHOT) {
-            ALOGV("%s: Camera %d: Discarding recording image buffers received after "
-                    "recording done",
-                    __FUNCTION__, mCameraId);
-            mRecordingConsumer->releaseBuffer(imgBuffer);
-            return;
-        }
-
-        if (mRecordingHeap == 0) {
-            const size_t bufferSize = 4 + sizeof(buffer_handle_t);
-            ALOGV("%s: Camera %d: Creating recording heap with %d buffers of "
-                    "size %d bytes", __FUNCTION__, mCameraId,
-                    mRecordingHeapCount, bufferSize);
-
-            mRecordingHeap = new Camera2Heap(bufferSize, mRecordingHeapCount,
-                    "Camera2Client::RecordingHeap");
-            if (mRecordingHeap->mHeap->getSize() == 0) {
-                ALOGE("%s: Camera %d: Unable to allocate memory for recording",
-                        __FUNCTION__, mCameraId);
-                mRecordingConsumer->releaseBuffer(imgBuffer);
-                return;
-            }
-            for (size_t i = 0; i < mRecordingBuffers.size(); i++) {
-                if (mRecordingBuffers[i].mBuf !=
-                        BufferItemConsumer::INVALID_BUFFER_SLOT) {
-                    ALOGE("%s: Camera %d: Non-empty recording buffers list!",
-                            __FUNCTION__, mCameraId);
-                }
-            }
-            mRecordingBuffers.clear();
-            mRecordingBuffers.setCapacity(mRecordingHeapCount);
-            mRecordingBuffers.insertAt(0, mRecordingHeapCount);
-
-            mRecordingHeapHead = 0;
-            mRecordingHeapFree = mRecordingHeapCount;
-        }
-
-        if ( mRecordingHeapFree == 0) {
-            ALOGE("%s: Camera %d: No free recording buffers, dropping frame",
-                    __FUNCTION__, mCameraId);
-            mRecordingConsumer->releaseBuffer(imgBuffer);
-            return;
-        }
-
-        heapIdx = mRecordingHeapHead;
-        mRecordingHeapHead = (mRecordingHeapHead + 1) % mRecordingHeapCount;
-        mRecordingHeapFree--;
-
-        ALOGV("%s: Camera %d: Timestamp %lld",
-                __FUNCTION__, mCameraId, timestamp);
-
-        ssize_t offset;
-        size_t size;
-        sp<IMemoryHeap> heap =
-                mRecordingHeap->mBuffers[heapIdx]->getMemory(&offset,
-                        &size);
-
-        uint8_t *data = (uint8_t*)heap->getBase() + offset;
-        uint32_t type = kMetadataBufferTypeGrallocSource;
-        *((uint32_t*)data) = type;
-        *((buffer_handle_t*)(data + 4)) = imgBuffer.mGraphicBuffer->handle;
-        ALOGV("%s: Camera %d: Sending out buffer_handle_t %p",
-                __FUNCTION__, mCameraId, imgBuffer.mGraphicBuffer->handle);
-        mRecordingBuffers.replaceAt(imgBuffer, heapIdx);
-        recordingHeap = mRecordingHeap;
-    }
-
-    // Call outside locked parameters to allow re-entrancy from notification
-    SharedCameraClient::Lock l(mSharedCameraClient);
-    if (l.mCameraClient != 0) {
-        l.mCameraClient->dataCallbackTimestamp(timestamp,
-                CAMERA_MSG_VIDEO_FRAME,
-                recordingHeap->mBuffers[heapIdx]);
-    }
-}
-
 /** Utility methods */
 
 status_t Camera2Client::updateRequests(Parameters &params) {
     status_t res;
 
-    res = updatePreviewRequest(params);
+    res = mStreamingProcessor->updatePreviewRequest(params);
     if (res != OK) {
         ALOGE("%s: Camera %d: Unable to update preview request: %s (%d)",
                 __FUNCTION__, mCameraId, strerror(-res), res);
         return res;
     }
-    res = updateRecordingRequest(params);
+    res = mStreamingProcessor->updateRecordingRequest(params);
     if (res != OK) {
         ALOGE("%s: Camera %d: Unable to update recording request: %s (%d)",
                 __FUNCTION__, mCameraId, strerror(-res), res);
@@ -1687,7 +1435,7 @@
         }
     } else if (params.state == Parameters::RECORD ||
             params.state == Parameters::VIDEO_SNAPSHOT) {
-        res = mDevice->setStreamingRequest(mRecordingRequest);
+        res = startRecordingL(params, true);
         if (res != OK) {
             ALOGE("%s: Camera %d: Error streaming new record request: %s (%d)",
                     __FUNCTION__, mCameraId, strerror(-res), res);
@@ -1697,172 +1445,6 @@
     return res;
 }
 
-status_t Camera2Client::updatePreviewStream(const Parameters &params) {
-    ATRACE_CALL();
-    status_t res;
-
-    if (mPreviewStreamId != NO_STREAM) {
-        // Check if stream parameters have to change
-        uint32_t currentWidth, currentHeight;
-        res = mDevice->getStreamInfo(mPreviewStreamId,
-                &currentWidth, &currentHeight, 0);
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Error querying preview stream info: "
-                    "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
-            return res;
-        }
-        if (currentWidth != (uint32_t)params.previewWidth ||
-                currentHeight != (uint32_t)params.previewHeight) {
-            ALOGV("%s: Camera %d: Preview size switch: %d x %d -> %d x %d",
-                    __FUNCTION__, mCameraId, currentWidth, currentHeight,
-                    params.previewWidth, params.previewHeight);
-            res = mDevice->waitUntilDrained();
-            if (res != OK) {
-                ALOGE("%s: Camera %d: Error waiting for preview to drain: "
-                        "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
-                return res;
-            }
-            res = mDevice->deleteStream(mPreviewStreamId);
-            if (res != OK) {
-                ALOGE("%s: Camera %d: Unable to delete old output stream "
-                        "for preview: %s (%d)", __FUNCTION__, mCameraId,
-                        strerror(-res), res);
-                return res;
-            }
-            mPreviewStreamId = NO_STREAM;
-        }
-    }
-
-    if (mPreviewStreamId == NO_STREAM) {
-        res = mDevice->createStream(mPreviewWindow,
-                params.previewWidth, params.previewHeight,
-                CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0,
-                &mPreviewStreamId);
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)",
-                    __FUNCTION__, mCameraId, strerror(-res), res);
-            return res;
-        }
-    }
-
-    res = mDevice->setStreamTransform(mPreviewStreamId,
-            params.previewTransform);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to set preview stream transform: "
-                "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
-        return res;
-    }
-
-    return OK;
-}
-
-status_t Camera2Client::updatePreviewRequest(const Parameters &params) {
-    ATRACE_CALL();
-    status_t res;
-    if (mPreviewRequest.entryCount() == 0) {
-        res = mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
-                &mPreviewRequest);
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Unable to create default preview request: "
-                    "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
-            return res;
-        }
-    }
-
-    res = params.updateRequest(&mPreviewRequest);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to update common entries of preview "
-                "request: %s (%d)", __FUNCTION__, mCameraId,
-                strerror(-res), res);
-        return res;
-    }
-
-    res = mPreviewRequest.update(ANDROID_REQUEST_ID,
-            &kPreviewRequestId, 1);
-
-    return OK;
-}
-
-status_t Camera2Client::updateRecordingRequest(const Parameters &params) {
-    ATRACE_CALL();
-    status_t res;
-    if (mRecordingRequest.entryCount() == 0) {
-        res = mDevice->createDefaultRequest(CAMERA2_TEMPLATE_VIDEO_RECORD,
-                &mRecordingRequest);
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Unable to create default recording request:"
-                    " %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
-            return res;
-        }
-    }
-
-    res = params.updateRequest(&mRecordingRequest);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to update common entries of recording "
-                "request: %s (%d)", __FUNCTION__, mCameraId,
-                strerror(-res), res);
-        return res;
-    }
-
-    return OK;
-}
-
-status_t Camera2Client::updateRecordingStream(const Parameters &params) {
-    status_t res;
-
-    if (mRecordingConsumer == 0) {
-        // Create CPU buffer queue endpoint. We need one more buffer here so that we can
-        // always acquire and free a buffer when the heap is full; otherwise the consumer
-        // will have buffers in flight we'll never clear out.
-        mRecordingConsumer = new BufferItemConsumer(
-                GRALLOC_USAGE_HW_VIDEO_ENCODER,
-                mRecordingHeapCount + 1,
-                true);
-        mRecordingConsumer->setFrameAvailableListener(new RecordingWaiter(this));
-        mRecordingConsumer->setName(String8("Camera2Client::RecordingConsumer"));
-        mRecordingWindow = new SurfaceTextureClient(
-            mRecordingConsumer->getProducerInterface());
-        // Allocate memory later, since we don't know buffer size until receipt
-    }
-
-    if (mRecordingStreamId != NO_STREAM) {
-        // Check if stream parameters have to change
-        uint32_t currentWidth, currentHeight;
-        res = mDevice->getStreamInfo(mRecordingStreamId,
-                &currentWidth, &currentHeight, 0);
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Error querying recording output stream info: "
-                    "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
-            return res;
-        }
-        if (currentWidth != (uint32_t)params.videoWidth ||
-                currentHeight != (uint32_t)params.videoHeight) {
-            // TODO: Should wait to be sure previous recording has finished
-            res = mDevice->deleteStream(mRecordingStreamId);
-            if (res != OK) {
-                ALOGE("%s: Camera %d: Unable to delete old output stream "
-                        "for recording: %s (%d)", __FUNCTION__, mCameraId,
-                        strerror(-res), res);
-                return res;
-            }
-            mRecordingStreamId = NO_STREAM;
-        }
-    }
-
-    if (mRecordingStreamId == NO_STREAM) {
-        mRecordingFrameCount = 0;
-        res = mDevice->createStream(mRecordingWindow,
-                params.videoWidth, params.videoHeight,
-                CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0, &mRecordingStreamId);
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Can't create output stream for recording: "
-                    "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
-            return res;
-        }
-    }
-
-    return OK;
-}
 
 size_t Camera2Client::calculateBufferSize(int width, int height,
         int format, int stride) {
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index 1eb024a..3a9d307 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -21,17 +21,15 @@
 #include "CameraService.h"
 #include "camera2/Parameters.h"
 #include "camera2/FrameProcessor.h"
+#include "camera2/StreamingProcessor.h"
 #include "camera2/JpegProcessor.h"
 #include "camera2/ZslProcessor.h"
 #include "camera2/CaptureSequencer.h"
 #include "camera2/CallbackProcessor.h"
-#include <binder/MemoryBase.h>
-#include <binder/MemoryHeapBase.h>
-#include <gui/CpuConsumer.h>
-#include <gui/BufferItemConsumer.h>
 
 namespace android {
 
+class IMemory;
 /**
  * Implements the android.hardware.camera API on top of
  * camera device HAL version 2.
@@ -184,15 +182,10 @@
 
     sp<camera2::FrameProcessor> mFrameProcessor;
 
-    /* Preview related members */
+    /* Preview/Recording related members */
 
-    int mPreviewStreamId;
-    CameraMetadata mPreviewRequest;
     sp<IBinder> mPreviewSurface;
-    sp<ANativeWindow> mPreviewWindow;
-
-    status_t updatePreviewRequest(const Parameters &params);
-    status_t updatePreviewStream(const Parameters &params);
+    sp<camera2::StreamingProcessor> mStreamingProcessor;
 
     /** Preview callback related members */
 
@@ -204,35 +197,6 @@
     sp<camera2::JpegProcessor> mJpegProcessor;
     sp<camera2::ZslProcessor> mZslProcessor;
 
-    /* Recording related members */
-
-    int mRecordingStreamId;
-    int mRecordingFrameCount;
-    sp<BufferItemConsumer>    mRecordingConsumer;
-    sp<ANativeWindow>  mRecordingWindow;
-    // Simple listener that forwards frame available notifications from
-    // a CPU consumer to the recording notification
-    class RecordingWaiter: public BufferItemConsumer::FrameAvailableListener {
-      public:
-        RecordingWaiter(Camera2Client *parent) : mParent(parent) {}
-        void onFrameAvailable() { mParent->onRecordingFrameAvailable(); }
-      private:
-        Camera2Client *mParent;
-    };
-    sp<RecordingWaiter>  mRecordingWaiter;
-    CameraMetadata mRecordingRequest;
-    sp<camera2::Camera2Heap> mRecordingHeap;
-
-    static const size_t kDefaultRecordingHeapCount = 8;
-    size_t mRecordingHeapCount;
-    Vector<BufferItemConsumer::BufferItem> mRecordingBuffers;
-    size_t mRecordingHeapHead, mRecordingHeapFree;
-    // Handle new recording image buffers
-    void onRecordingFrameAvailable();
-
-    status_t updateRecordingRequest(const Parameters &params);
-    status_t updateRecordingStream(const Parameters &params);
-
     /** Notification-related members */
 
     bool mAfInMotion;
diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.cpp b/services/camera/libcameraservice/camera2/JpegProcessor.cpp
index a40ddcc..7b368fa 100644
--- a/services/camera/libcameraservice/camera2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/camera2/JpegProcessor.cpp
@@ -20,6 +20,8 @@
 
 #include <netinet/in.h>
 
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
 
diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.h b/services/camera/libcameraservice/camera2/JpegProcessor.h
index 5464519..836bd02 100644
--- a/services/camera/libcameraservice/camera2/JpegProcessor.h
+++ b/services/camera/libcameraservice/camera2/JpegProcessor.h
@@ -25,11 +25,11 @@
 #include <gui/CpuConsumer.h>
 #include "Parameters.h"
 #include "CameraMetadata.h"
-#include "Camera2Heap.h"
 
 namespace android {
 
 class Camera2Client;
+class MemoryHeapBase;
 
 namespace camera2 {
 
diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.cpp b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp
new file mode 100644
index 0000000..140138d
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp
@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2012 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 "Camera2-StreamingProcessor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <gui/SurfaceTextureClient.h>
+#include <media/hardware/MetadataBufferType.h>
+
+#include "StreamingProcessor.h"
+#include "Camera2Heap.h"
+#include "../Camera2Client.h"
+#include "../Camera2Device.h"
+
+namespace android {
+namespace camera2 {
+
+StreamingProcessor::StreamingProcessor(wp<Camera2Client> client):
+        mClient(client),
+        mPreviewStreamId(NO_STREAM),
+        mRecordingStreamId(NO_STREAM),
+        mRecordingHeapCount(kDefaultRecordingHeapCount)
+{
+
+}
+
+StreamingProcessor::~StreamingProcessor() {
+    deletePreviewStream();
+    deleteRecordingStream();
+}
+
+status_t StreamingProcessor::setPreviewWindow(sp<ANativeWindow> window) {
+    ATRACE_CALL();
+    status_t res;
+
+    res = deletePreviewStream();
+    if (res != OK) return res;
+
+    Mutex::Autolock m(mMutex);
+
+    mPreviewWindow = window;
+
+    return OK;
+}
+
+bool StreamingProcessor::haveValidPreviewWindow() const {
+    Mutex::Autolock m(mMutex);
+    return mPreviewWindow != 0;
+}
+
+status_t StreamingProcessor::updatePreviewRequest(const Parameters &params) {
+    ATRACE_CALL();
+    status_t res;
+    sp<Camera2Client> client = mClient.promote();
+    if (client == 0) return INVALID_OPERATION;
+
+    Mutex::Autolock m(mMutex);
+    if (mPreviewRequest.entryCount() == 0) {
+        res = client->getCameraDevice()->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+                &mPreviewRequest);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Unable to create default preview request: "
+                    "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res);
+            return res;
+        }
+    }
+
+    res = params.updateRequest(&mPreviewRequest);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to update common entries of preview "
+                "request: %s (%d)", __FUNCTION__, client->getCameraId(),
+                strerror(-res), res);
+        return res;
+    }
+
+    res = mPreviewRequest.update(ANDROID_REQUEST_ID,
+            &Camera2Client::kPreviewRequestId, 1);
+
+    return OK;
+}
+
+status_t StreamingProcessor::updatePreviewStream(const Parameters &params) {
+    ATRACE_CALL();
+    Mutex::Autolock m(mMutex);
+
+    status_t res;
+    sp<Camera2Client> client = mClient.promote();
+    if (client == 0) return INVALID_OPERATION;
+    sp<Camera2Device> device = client->getCameraDevice();
+
+    if (mPreviewStreamId != NO_STREAM) {
+        // Check if stream parameters have to change
+        uint32_t currentWidth, currentHeight;
+        res = device->getStreamInfo(mPreviewStreamId,
+                &currentWidth, &currentHeight, 0);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Error querying preview stream info: "
+                    "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res);
+            return res;
+        }
+        if (currentWidth != (uint32_t)params.previewWidth ||
+                currentHeight != (uint32_t)params.previewHeight) {
+            ALOGV("%s: Camera %d: Preview size switch: %d x %d -> %d x %d",
+                    __FUNCTION__, client->getCameraId(), currentWidth, currentHeight,
+                    params.previewWidth, params.previewHeight);
+            res = device->waitUntilDrained();
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Error waiting for preview to drain: "
+                        "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res);
+                return res;
+            }
+            res = device->deleteStream(mPreviewStreamId);
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Unable to delete old output stream "
+                        "for preview: %s (%d)", __FUNCTION__, client->getCameraId(),
+                        strerror(-res), res);
+                return res;
+            }
+            mPreviewStreamId = NO_STREAM;
+        }
+    }
+
+    if (mPreviewStreamId == NO_STREAM) {
+        res = device->createStream(mPreviewWindow,
+                params.previewWidth, params.previewHeight,
+                CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0,
+                &mPreviewStreamId);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)",
+                    __FUNCTION__, client->getCameraId(), strerror(-res), res);
+            return res;
+        }
+    }
+
+    res = device->setStreamTransform(mPreviewStreamId,
+            params.previewTransform);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to set preview stream transform: "
+                "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res);
+        return res;
+    }
+
+    return OK;
+}
+
+status_t StreamingProcessor::deletePreviewStream() {
+    ATRACE_CALL();
+    status_t res;
+
+    Mutex::Autolock m(mMutex);
+
+    if (mPreviewStreamId != NO_STREAM) {
+        sp<Camera2Client> client = mClient.promote();
+        if (client == 0) return INVALID_OPERATION;
+        sp<Camera2Device> device = client->getCameraDevice();
+
+        res = device->waitUntilDrained();
+        if (res != OK) {
+            ALOGE("%s: Error waiting for preview to drain: %s (%d)",
+                    __FUNCTION__, strerror(-res), res);
+            return res;
+        }
+        res = device->deleteStream(mPreviewStreamId);
+        if (res != OK) {
+            ALOGE("%s: Unable to delete old preview stream: %s (%d)",
+                    __FUNCTION__, strerror(-res), res);
+            return res;
+        }
+        mPreviewStreamId = NO_STREAM;
+    }
+    return OK;
+}
+
+status_t StreamingProcessor::getPreviewStreamId() const {
+    Mutex::Autolock m(mMutex);
+    return mPreviewStreamId;
+}
+
+status_t StreamingProcessor::setRecordingBufferCount(size_t count) {
+    ATRACE_CALL();
+    // 32 is the current upper limit on the video buffer count for BufferQueue
+    sp<Camera2Client> client = mClient.promote();
+    if (client == 0) return INVALID_OPERATION;
+    if (count > 32) {
+        ALOGE("%s: Camera %d: Error setting %d as video buffer count value",
+                __FUNCTION__, client->getCameraId(), count);
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock m(mMutex);
+
+    // Need to reallocate memory for heap
+    if (mRecordingHeapCount != count) {
+        if  (mRecordingHeap != 0) {
+            mRecordingHeap.clear();
+            mRecordingHeap = NULL;
+        }
+        mRecordingHeapCount = count;
+    }
+
+    return OK;
+}
+
+status_t StreamingProcessor::updateRecordingRequest(const Parameters &params) {
+    ATRACE_CALL();
+    status_t res;
+    Mutex::Autolock m(mMutex);
+
+    sp<Camera2Client> client = mClient.promote();
+    if (client == 0) return INVALID_OPERATION;
+
+    if (mRecordingRequest.entryCount() == 0) {
+        res = client->getCameraDevice()->createDefaultRequest(CAMERA2_TEMPLATE_VIDEO_RECORD,
+                &mRecordingRequest);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Unable to create default recording request:"
+                    " %s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res);
+            return res;
+        }
+    }
+
+    res = params.updateRequest(&mRecordingRequest);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to update common entries of recording "
+                "request: %s (%d)", __FUNCTION__, client->getCameraId(),
+                strerror(-res), res);
+        return res;
+    }
+
+    return OK;
+}
+
+status_t StreamingProcessor::updateRecordingStream(const Parameters &params) {
+    ATRACE_CALL();
+    status_t res;
+    Mutex::Autolock m(mMutex);
+
+    sp<Camera2Client> client = mClient.promote();
+    if (client == 0) return INVALID_OPERATION;
+    sp<Camera2Device> device = client->getCameraDevice();
+
+    if (mRecordingConsumer == 0) {
+        // Create CPU buffer queue endpoint. We need one more buffer here so that we can
+        // always acquire and free a buffer when the heap is full; otherwise the consumer
+        // will have buffers in flight we'll never clear out.
+        mRecordingConsumer = new BufferItemConsumer(
+                GRALLOC_USAGE_HW_VIDEO_ENCODER,
+                mRecordingHeapCount + 1,
+                true);
+        mRecordingConsumer->setFrameAvailableListener(this);
+        mRecordingConsumer->setName(String8("Camera2-RecordingConsumer"));
+        mRecordingWindow = new SurfaceTextureClient(
+            mRecordingConsumer->getProducerInterface());
+        // Allocate memory later, since we don't know buffer size until receipt
+    }
+
+    if (mRecordingStreamId != NO_STREAM) {
+        // Check if stream parameters have to change
+        uint32_t currentWidth, currentHeight;
+        res = device->getStreamInfo(mRecordingStreamId,
+                &currentWidth, &currentHeight, 0);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Error querying recording output stream info: "
+                    "%s (%d)", __FUNCTION__, client->getCameraId(),
+                    strerror(-res), res);
+            return res;
+        }
+        if (currentWidth != (uint32_t)params.videoWidth ||
+                currentHeight != (uint32_t)params.videoHeight) {
+            // TODO: Should wait to be sure previous recording has finished
+            res = device->deleteStream(mRecordingStreamId);
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Unable to delete old output stream "
+                        "for recording: %s (%d)", __FUNCTION__,
+                        client->getCameraId(), strerror(-res), res);
+                return res;
+            }
+            mRecordingStreamId = NO_STREAM;
+        }
+    }
+
+    if (mRecordingStreamId == NO_STREAM) {
+        mRecordingFrameCount = 0;
+        res = device->createStream(mRecordingWindow,
+                params.videoWidth, params.videoHeight,
+                CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0, &mRecordingStreamId);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Can't create output stream for recording: "
+                    "%s (%d)", __FUNCTION__, client->getCameraId(),
+                    strerror(-res), res);
+            return res;
+        }
+    }
+
+    return OK;
+}
+
+status_t StreamingProcessor::deleteRecordingStream() {
+    ATRACE_CALL();
+    status_t res;
+
+    Mutex::Autolock m(mMutex);
+
+    if (mRecordingStreamId != NO_STREAM) {
+        sp<Camera2Client> client = mClient.promote();
+        if (client == 0) return INVALID_OPERATION;
+        sp<Camera2Device> device = client->getCameraDevice();
+
+        res = device->waitUntilDrained();
+        if (res != OK) {
+            ALOGE("%s: Error waiting for HAL to drain: %s (%d)",
+                    __FUNCTION__, strerror(-res), res);
+            return res;
+        }
+        res = device->deleteStream(mRecordingStreamId);
+        if (res != OK) {
+            ALOGE("%s: Unable to delete recording stream: %s (%d)",
+                    __FUNCTION__, strerror(-res), res);
+            return res;
+        }
+        mRecordingStreamId = NO_STREAM;
+    }
+    return OK;
+}
+
+status_t StreamingProcessor::getRecordingStreamId() const {
+    return mRecordingStreamId;
+}
+
+status_t StreamingProcessor::startStream(StreamType type,
+        const Vector<uint8_t> &outputStreams) {
+    ATRACE_CALL();
+    status_t res;
+
+    sp<Camera2Client> client = mClient.promote();
+    if (client == 0) return INVALID_OPERATION;
+
+    Mutex::Autolock m(mMutex);
+
+    CameraMetadata &request = (type == PREVIEW) ?
+            mPreviewRequest : mRecordingRequest;
+
+    res = request.update(
+        ANDROID_REQUEST_OUTPUT_STREAMS,
+        outputStreams);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to set up preview request: %s (%d)",
+                __FUNCTION__, client->getCameraId(), strerror(-res), res);
+        return res;
+    }
+
+    res = request.sort();
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Error sorting preview request: %s (%d)",
+                __FUNCTION__, client->getCameraId(), strerror(-res), res);
+        return res;
+    }
+
+    res = client->getCameraDevice()->setStreamingRequest(request);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to set preview request to start preview: "
+                "%s (%d)",
+                __FUNCTION__, client->getCameraId(), strerror(-res), res);
+        return res;
+    }
+
+    return OK;
+}
+
+status_t StreamingProcessor::stopStream() {
+    ATRACE_CALL();
+    status_t res;
+
+    sp<Camera2Client> client = mClient.promote();
+    if (client == 0) return INVALID_OPERATION;
+    sp<Camera2Device> device = client->getCameraDevice();
+
+    res = device->clearStreamingRequest();
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Can't clear stream request: %s (%d)",
+                __FUNCTION__, client->getCameraId(), strerror(-res), res);
+        return res;
+    }
+    res = device->waitUntilDrained();
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)",
+                __FUNCTION__, client->getCameraId(), strerror(-res), res);
+        return res;
+    }
+    return OK;
+}
+
+void StreamingProcessor::onFrameAvailable() {
+    ATRACE_CALL();
+    status_t res;
+    sp<Camera2Heap> recordingHeap;
+    size_t heapIdx = 0;
+    nsecs_t timestamp;
+
+    sp<Camera2Client> client = mClient.promote();
+    if (client == 0) return;
+
+    {
+        Mutex::Autolock m(mMutex);
+        BufferItemConsumer::BufferItem imgBuffer;
+        res = mRecordingConsumer->acquireBuffer(&imgBuffer);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Error receiving recording buffer: %s (%d)",
+                    __FUNCTION__, client->getCameraId(), strerror(-res), res);
+            return;
+        }
+        timestamp = imgBuffer.mTimestamp;
+
+        mRecordingFrameCount++;
+        ALOGV("OnRecordingFrame: Frame %d", mRecordingFrameCount);
+
+        {
+            SharedParameters::Lock l(client->getParameters());
+            // TODO: Signal errors here upstream
+            if (l.mParameters.state != Parameters::RECORD &&
+                    l.mParameters.state != Parameters::VIDEO_SNAPSHOT) {
+                ALOGV("%s: Camera %d: Discarding recording image buffers "
+                        "received after recording done", __FUNCTION__,
+                        client->getCameraId());
+                mRecordingConsumer->releaseBuffer(imgBuffer);
+                return;
+            }
+        }
+
+        if (mRecordingHeap == 0) {
+            const size_t bufferSize = 4 + sizeof(buffer_handle_t);
+            ALOGV("%s: Camera %d: Creating recording heap with %d buffers of "
+                    "size %d bytes", __FUNCTION__, client->getCameraId(),
+                    mRecordingHeapCount, bufferSize);
+
+            mRecordingHeap = new Camera2Heap(bufferSize, mRecordingHeapCount,
+                    "Camera2Client::RecordingHeap");
+            if (mRecordingHeap->mHeap->getSize() == 0) {
+                ALOGE("%s: Camera %d: Unable to allocate memory for recording",
+                        __FUNCTION__, client->getCameraId());
+                mRecordingConsumer->releaseBuffer(imgBuffer);
+                return;
+            }
+            for (size_t i = 0; i < mRecordingBuffers.size(); i++) {
+                if (mRecordingBuffers[i].mBuf !=
+                        BufferItemConsumer::INVALID_BUFFER_SLOT) {
+                    ALOGE("%s: Camera %d: Non-empty recording buffers list!",
+                            __FUNCTION__, client->getCameraId());
+                }
+            }
+            mRecordingBuffers.clear();
+            mRecordingBuffers.setCapacity(mRecordingHeapCount);
+            mRecordingBuffers.insertAt(0, mRecordingHeapCount);
+
+            mRecordingHeapHead = 0;
+            mRecordingHeapFree = mRecordingHeapCount;
+        }
+
+        if ( mRecordingHeapFree == 0) {
+            ALOGE("%s: Camera %d: No free recording buffers, dropping frame",
+                    __FUNCTION__, client->getCameraId());
+            mRecordingConsumer->releaseBuffer(imgBuffer);
+            return;
+        }
+
+        heapIdx = mRecordingHeapHead;
+        mRecordingHeapHead = (mRecordingHeapHead + 1) % mRecordingHeapCount;
+        mRecordingHeapFree--;
+
+        ALOGV("%s: Camera %d: Timestamp %lld",
+                __FUNCTION__, client->getCameraId(), timestamp);
+
+        ssize_t offset;
+        size_t size;
+        sp<IMemoryHeap> heap =
+                mRecordingHeap->mBuffers[heapIdx]->getMemory(&offset,
+                        &size);
+
+        uint8_t *data = (uint8_t*)heap->getBase() + offset;
+        uint32_t type = kMetadataBufferTypeGrallocSource;
+        *((uint32_t*)data) = type;
+        *((buffer_handle_t*)(data + 4)) = imgBuffer.mGraphicBuffer->handle;
+        ALOGV("%s: Camera %d: Sending out buffer_handle_t %p",
+                __FUNCTION__, client->getCameraId(),
+                imgBuffer.mGraphicBuffer->handle);
+        mRecordingBuffers.replaceAt(imgBuffer, heapIdx);
+        recordingHeap = mRecordingHeap;
+    }
+
+    // Call outside locked parameters to allow re-entrancy from notification
+    Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient);
+    if (l.mCameraClient != 0) {
+        l.mCameraClient->dataCallbackTimestamp(timestamp,
+                CAMERA_MSG_VIDEO_FRAME,
+                recordingHeap->mBuffers[heapIdx]);
+    }
+}
+
+void StreamingProcessor::releaseRecordingFrame(const sp<IMemory>& mem) {
+    ATRACE_CALL();
+    status_t res;
+
+    sp<Camera2Client> client = mClient.promote();
+    if (client == 0) return;
+
+    Mutex::Autolock m(mMutex);
+    // Make sure this is for the current heap
+    ssize_t offset;
+    size_t size;
+    sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+    if (heap->getHeapID() != mRecordingHeap->mHeap->getHeapID()) {
+        ALOGW("%s: Camera %d: Mismatched heap ID, ignoring release "
+                "(got %x, expected %x)", __FUNCTION__, client->getCameraId(),
+                heap->getHeapID(), mRecordingHeap->mHeap->getHeapID());
+        return;
+    }
+    uint8_t *data = (uint8_t*)heap->getBase() + offset;
+    uint32_t type = *(uint32_t*)data;
+    if (type != kMetadataBufferTypeGrallocSource) {
+        ALOGE("%s: Camera %d: Recording frame type invalid (got %x, expected %x)",
+                __FUNCTION__, client->getCameraId(), type,
+                kMetadataBufferTypeGrallocSource);
+        return;
+    }
+
+    // Release the buffer back to the recording queue
+
+    buffer_handle_t imgHandle = *(buffer_handle_t*)(data + 4);
+
+    size_t itemIndex;
+    for (itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) {
+        const BufferItemConsumer::BufferItem item =
+                mRecordingBuffers[itemIndex];
+        if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT &&
+                item.mGraphicBuffer->handle == imgHandle) {
+            break;
+        }
+    }
+    if (itemIndex == mRecordingBuffers.size()) {
+        ALOGE("%s: Camera %d: Can't find buffer_handle_t %p in list of "
+                "outstanding buffers", __FUNCTION__, client->getCameraId(),
+                imgHandle);
+        return;
+    }
+
+    ALOGV("%s: Camera %d: Freeing buffer_handle_t %p", __FUNCTION__,
+            client->getCameraId(), imgHandle);
+
+    res = mRecordingConsumer->releaseBuffer(mRecordingBuffers[itemIndex]);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to free recording frame "
+                "(buffer_handle_t: %p): %s (%d)", __FUNCTION__,
+                client->getCameraId(), imgHandle, strerror(-res), res);
+        return;
+    }
+    mRecordingBuffers.replaceAt(itemIndex);
+
+    mRecordingHeapFree++;
+}
+
+
+status_t StreamingProcessor::dump(int fd, const Vector<String16>& args) {
+    String8 result;
+
+    result.append("  Current requests:\n");
+    if (mPreviewRequest.entryCount() != 0) {
+        result.append("    Preview request:\n");
+        write(fd, result.string(), result.size());
+        mPreviewRequest.dump(fd, 2, 6);
+    } else {
+        result.append("    Preview request: undefined\n");
+        write(fd, result.string(), result.size());
+    }
+
+    if (mRecordingRequest.entryCount() != 0) {
+        result = "    Recording request:\n";
+        write(fd, result.string(), result.size());
+        mRecordingRequest.dump(fd, 2, 6);
+    } else {
+        result = "    Recording request: undefined\n";
+        write(fd, result.string(), result.size());
+    }
+
+    return OK;
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.h b/services/camera/libcameraservice/camera2/StreamingProcessor.h
new file mode 100644
index 0000000..ac58614
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/StreamingProcessor.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2012 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 ANDROID_SERVERS_CAMERA_CAMERA2_STREAMINGPROCESSOR_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2_STREAMINGPROCESSOR_H
+
+#include <utils/Mutex.h>
+#include <utils/String16.h>
+#include <gui/BufferItemConsumer.h>
+
+#include "Parameters.h"
+#include "CameraMetadata.h"
+
+namespace android {
+
+class Camera2Client;
+class IMemory;
+
+namespace camera2 {
+
+class Camera2Heap;
+
+/**
+ * Management and processing for preview and recording streams
+ */
+class StreamingProcessor: public BufferItemConsumer::FrameAvailableListener {
+  public:
+    StreamingProcessor(wp<Camera2Client> client);
+    ~StreamingProcessor();
+
+    status_t setPreviewWindow(sp<ANativeWindow> window);
+
+    bool haveValidPreviewWindow() const;
+
+    status_t updatePreviewRequest(const Parameters &params);
+    status_t updatePreviewStream(const Parameters &params);
+    status_t deletePreviewStream();
+    int getPreviewStreamId() const;
+
+    status_t setRecordingBufferCount(size_t count);
+    status_t updateRecordingRequest(const Parameters &params);
+    status_t updateRecordingStream(const Parameters &params);
+    status_t deleteRecordingStream();
+    int getRecordingStreamId() const;
+
+    enum StreamType {
+        PREVIEW,
+        RECORD
+    };
+    status_t startStream(StreamType type,
+            const Vector<uint8_t> &outputStreams);
+
+    status_t stopStream();
+
+    // Callback for new recording frames from HAL
+    virtual void onFrameAvailable();
+    // Callback from stagefright which returns used recording frames
+    void releaseRecordingFrame(const sp<IMemory>& mem);
+
+    status_t dump(int fd, const Vector<String16>& args);
+
+  private:
+    mutable Mutex mMutex;
+
+    enum {
+        NO_STREAM = -1
+    };
+
+    wp<Camera2Client> mClient;
+
+    // Preview-related members
+    int mPreviewStreamId;
+    CameraMetadata mPreviewRequest;
+    sp<ANativeWindow> mPreviewWindow;
+
+    // Recording-related members
+    int mRecordingStreamId;
+    int mRecordingFrameCount;
+    sp<BufferItemConsumer> mRecordingConsumer;
+    sp<ANativeWindow>  mRecordingWindow;
+    CameraMetadata mRecordingRequest;
+    sp<camera2::Camera2Heap> mRecordingHeap;
+
+    static const size_t kDefaultRecordingHeapCount = 8;
+    size_t mRecordingHeapCount;
+    Vector<BufferItemConsumer::BufferItem> mRecordingBuffers;
+    size_t mRecordingHeapHead, mRecordingHeapFree;
+
+};
+
+
+}; // namespace camera2
+}; // namespace android
+
+#endif