Camera: ProCamera - add createStreamCpu and unit test

Change-Id: I468172dbfdd78510b273bf9d119c950cbeda7ea3
diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp
index 8fd08f4..5ee0e4d 100644
--- a/camera/ProCamera.cpp
+++ b/camera/ProCamera.cpp
@@ -246,19 +246,16 @@
     return c->cancelRequest(requestId);
 }
 
-status_t ProCamera::requestStream(int streamId)
+status_t ProCamera::deleteStream(int streamId)
 {
     sp <IProCameraUser> c = mCamera;
     if (c == 0) return NO_INIT;
 
-    return c->requestStream(streamId);
-}
-status_t ProCamera::cancelStream(int streamId)
-{
-    sp <IProCameraUser> c = mCamera;
-    if (c == 0) return NO_INIT;
+    status_t s = c->cancelStream(streamId);
 
-    return c->cancelStream(streamId);
+    mStreams.removeItem(streamId);
+
+    return s;
 }
 
 status_t ProCamera::createStream(int width, int height, int format,
@@ -275,38 +272,76 @@
         return BAD_VALUE;
     }
 
-    sp <IProCameraUser> c = mCamera;
-    if (c == 0) return NO_INIT;
-
-    return c->createStream(width, height, format, surface->getIGraphicBufferProducer(),
-                           streamId);
+    return createStream(width, height, format, surface->getIGraphicBufferProducer(),
+                        streamId);
 }
 
 status_t ProCamera::createStream(int width, int height, int format,
                           const sp<IGraphicBufferProducer>& bufferProducer,
                           /*out*/
                           int* streamId) {
+    *streamId = -1;
 
     ALOGV("%s: createStreamT %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
                                                                        format);
 
-    sp<IBinder> binder;
-    status_t stat = INVALID_OPERATION;
-
-    if (bufferProducer != 0) {
-        sp <IProCameraUser> c = mCamera;
-        if (c == 0) return NO_INIT;
-
-        return c->createStream(width, height, format, bufferProducer, streamId);
-    }
-    else {
-        *streamId = -1;
+    if (bufferProducer == 0) {
         return BAD_VALUE;
     }
 
+    sp <IProCameraUser> c = mCamera;
+    status_t stat = c->createStream(width, height, format, bufferProducer,
+                                    streamId);
+
+    if (stat == OK) {
+        StreamInfo s(*streamId);
+
+        mStreams.add(*streamId, s);
+    }
+
     return stat;
 }
 
+status_t ProCamera::createStreamCpu(int width, int height, int format,
+                          int heapCount,
+                          /*out*/
+                          int* streamId)
+{
+    ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
+                                                                        format);
+
+    sp <IProCameraUser> c = mCamera;
+    if (c == 0) return NO_INIT;
+
+    sp<CpuConsumer> cc = new CpuConsumer(heapCount);
+    cc->setName(String8("ProCamera::mCpuConsumer"));
+
+    sp<Surface> stc = new Surface(
+        cc->getProducerInterface());
+
+    status_t s = createStream(width, height, format, stc->getIGraphicBufferProducer(),
+                        streamId);
+
+    if (s != OK) {
+        ALOGE("%s: Failure to create stream %dx%d (fmt=0x%x)", __FUNCTION__,
+                    width, height, format);
+        return s;
+    }
+
+    sp<ProFrameListener> frameAvailableListener =
+        new ProFrameListener(this, *streamId);
+
+    getStreamInfo(*streamId).cpuStream = true;
+    getStreamInfo(*streamId).cpuConsumer = cc;
+    getStreamInfo(*streamId).stc = stc;
+    // for lifetime management
+    getStreamInfo(*streamId).frameAvailableListener = frameAvailableListener;
+
+    cc->setFrameAvailableListener(frameAvailableListener);
+
+    return s;
+}
+
 int ProCamera::getNumberOfCameras() {
     ALOGE("%s: not implemented yet", __FUNCTION__);
     return 1;
@@ -329,4 +364,34 @@
     return c->createDefaultRequest(templateId, request);
 }
 
+void ProCamera::onFrameAvailable(int streamId) {
+    ALOGV("%s: streamId = %d", __FUNCTION__, streamId);
+
+    sp<ProCameraListener> listener = mListener;
+    if (listener.get() != NULL) {
+        StreamInfo& stream = getStreamInfo(streamId);
+
+        CpuConsumer::LockedBuffer buf;
+
+        status_t stat = stream.cpuConsumer->lockNextBuffer(&buf);
+        if (stat != OK) {
+            ALOGE("%s: Failed to lock buffer, error code = %d", __FUNCTION__,
+                   stat);
+            return;
+        }
+
+        listener->onBufferReceived(streamId, buf);
+        stat = stream.cpuConsumer->unlockBuffer(buf);
+
+        if (stat != OK) {
+            ALOGE("%s: Failed to unlock buffer, error code = %d", __FUNCTION__,
+                   stat);
+        }
+    }
+}
+
+ProCamera::StreamInfo& ProCamera::getStreamInfo(int streamId) {
+    return mStreams.editValueFor(streamId);
+}
+
 }; // namespace android
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
index 230e160..f0a36e8 100644
--- a/camera/tests/ProCameraTests.cpp
+++ b/camera/tests/ProCameraTests.cpp
@@ -41,7 +41,12 @@
 #define TEST_DEBUGGING 0
 
 #define TEST_LISTENER_TIMEOUT 1000000000 // 1 second listener timeout
-#define TEST_FORMAT HAL_PIXEL_FORMAT_RGBA_8888 //TODO: YUY2 instead
+#define TEST_FORMAT HAL_PIXEL_FORMAT_Y16 //TODO: YUY2 instead
+
+#define TEST_FORMAT_DEPTH HAL_PIXEL_FORMAT_Y16
+
+#define TEST_CPU_FRAME_COUNT 2
+#define TEST_CPU_HEAP_COUNT 5
 
 #if TEST_DEBUGGING
 #define dout std::cerr
@@ -54,14 +59,15 @@
 
 class ProCameraTest;
 
-enum LockEvent {
+enum ProEvent {
     UNKNOWN,
     ACQUIRED,
     RELEASED,
-    STOLEN
+    STOLEN,
+    BUFFER_RECEIVED,
 };
 
-typedef Vector<LockEvent> EventList;
+typedef Vector<ProEvent> EventList;
 
 class ProCameraTestThread : public Thread
 {
@@ -92,7 +98,7 @@
         {
             Mutex::Autolock al(mListenerMutex);
 
-            if (mLockEventList.size() > 0) {
+            if (mProEventList.size() > 0) {
                 return OK;
             }
         }
@@ -105,35 +111,35 @@
     void ReadEvents(EventList& out) {
         Mutex::Autolock al(mListenerMutex);
 
-        for (size_t i = 0; i < mLockEventList.size(); ++i) {
-            out.push(mLockEventList[i]);
+        for (size_t i = 0; i < mProEventList.size(); ++i) {
+            out.push(mProEventList[i]);
         }
 
-        mLockEventList.clear();
+        mProEventList.clear();
     }
 
     /**
       * Dequeue 1 event from the event queue.
       * Returns UNKNOWN if queue is empty
       */
-    LockEvent ReadEvent() {
+    ProEvent ReadEvent() {
         Mutex::Autolock al(mListenerMutex);
 
-        if (mLockEventList.size() == 0) {
+        if (mProEventList.size() == 0) {
             return UNKNOWN;
         }
 
-        LockEvent ev = mLockEventList[0];
-        mLockEventList.removeAt(0);
+        ProEvent ev = mProEventList[0];
+        mProEventList.removeAt(0);
 
         return ev;
     }
 
 private:
-    void QueueEvent(LockEvent ev) {
+    void QueueEvent(ProEvent ev) {
         {
             Mutex::Autolock al(mListenerMutex);
-            mLockEventList.push(ev);
+            mProEventList.push(ev);
         }
 
 
@@ -168,6 +174,20 @@
              << " " << ext3 << std::endl;
     }
 
+    virtual void onBufferReceived(int streamId,
+                                  const CpuConsumer::LockedBuffer& buf) {
+
+        dout << "Buffer received on streamId = " << streamId <<
+                ", dataPtr = " << (void*)buf.data << std::endl;
+
+        QueueEvent(BUFFER_RECEIVED);
+
+    }
+    virtual void onRequestReceived(
+                                   camera_metadata* request) {
+        free_camera_metadata(request);
+    }
+
     // TODO: remove
 
     virtual void notify(int32_t , int32_t , int32_t ) {}
@@ -176,7 +196,7 @@
     virtual void postDataTimestamp(nsecs_t , int32_t , const sp<IMemory>& ) {}
 
 
-    Vector<LockEvent> mLockEventList;
+    Vector<ProEvent> mProEventList;
     Mutex             mListenerMutex;
     Mutex             mConditionMutex;
     Condition         mListenerCondition;
@@ -217,6 +237,9 @@
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mSurfaceControl;
 
+    sp<SurfaceComposerClient> mDepthComposerClient;
+    sp<SurfaceControl> mDepthSurfaceControl;
+
     int getSurfaceWidth() {
         return 512;
     }
@@ -233,6 +256,8 @@
                 getSurfaceWidth(), getSurfaceHeight(),
                 PIXEL_FORMAT_RGB_888, 0);
 
+        mSurfaceControl->setPosition(640, 0);
+
         ASSERT_TRUE(mSurfaceControl != NULL);
         ASSERT_TRUE(mSurfaceControl->isValid());
 
@@ -247,6 +272,31 @@
         ASSERT_NE((void*)NULL, surface.get());
     }
 
+    void createDepthOnScreenSurface(sp<Surface>& surface) {
+        mDepthComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mDepthComposerClient->initCheck());
+
+        mDepthSurfaceControl = mDepthComposerClient->createSurface(
+                String8("ProCameraTest StreamingImage Surface"),
+                getSurfaceWidth(), getSurfaceHeight(),
+                PIXEL_FORMAT_RGB_888, 0);
+
+        mDepthSurfaceControl->setPosition(640, 0);
+
+        ASSERT_TRUE(mDepthSurfaceControl != NULL);
+        ASSERT_TRUE(mDepthSurfaceControl->isValid());
+
+        SurfaceComposerClient::openGlobalTransaction();
+        ASSERT_EQ(NO_ERROR, mDepthSurfaceControl->setLayer(0x7FFFFFFF));
+        ASSERT_EQ(NO_ERROR, mDepthSurfaceControl->show());
+        SurfaceComposerClient::closeGlobalTransaction();
+
+        sp<ANativeWindow> window = mDepthSurfaceControl->getSurface();
+        surface = mDepthSurfaceControl->getSurface();
+
+        ASSERT_NE((void*)NULL, surface.get());
+    }
+
 };
 
 sp<Thread> ProCameraTest::mTestThread;
@@ -316,14 +366,15 @@
         mDisplaySecs = 0;
     }
 
-    sp<Surface> surface;
+    sp<Surface> depthSurface;
     if (mDisplaySecs > 0) {
-        createOnScreenSurface(/*out*/surface);
+        createDepthOnScreenSurface(/*out*/depthSurface);
     }
-    int streamId = -1;
-    EXPECT_OK(mCamera->createStream(/*width*/640, /*height*/480, TEST_FORMAT,
-              surface, &streamId));
-    EXPECT_NE(-1, streamId);
+
+    int depthStreamId = -1;
+    EXPECT_OK(mCamera->createStream(/*width*/320, /*height*/240,
+                              TEST_FORMAT_DEPTH, depthSurface, &depthStreamId));
+    EXPECT_NE(-1, depthStreamId);
 
     EXPECT_OK(mCamera->exclusiveTryLock());
     /* iterate in a loop submitting requests every frame.
@@ -345,23 +396,26 @@
     // wow what a verbose API.
     // i would give a loaf of bread for
     //   metadata->updateOrInsert(keys.request.output.streams, streamId);
+    uint8_t allStreams[] = { depthStreamId };
+    size_t streamCount = sizeof(allStreams) / sizeof(allStreams[0]);
+
     camera_metadata_entry_t entry;
     uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
     int find = find_camera_metadata_entry(request, tag, &entry);
     if (find == -ENOENT) {
-        if (add_camera_metadata_entry(request, tag, &streamId, /*data_count*/1)
-                != OK) {
+        if (add_camera_metadata_entry(request, tag, &allStreams,
+            /*data_count*/streamCount) != OK) {
             camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
             ASSERT_OK(append_camera_metadata(tmp, request));
             free_camera_metadata(request);
             request = tmp;
 
-            ASSERT_OK(add_camera_metadata_entry(request, tag, &streamId,
-                /*data_count*/1));
+            ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
+                /*data_count*/streamCount));
         }
     } else {
-        ASSERT_OK(update_camera_metadata_entry(request, entry.index, &streamId,
-                  /*data_count*/1, &entry));
+        ASSERT_OK(update_camera_metadata_entry(request, entry.index,
+                &allStreams, /*data_count*/streamCount, &entry));
     }
 
     EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
@@ -370,7 +424,72 @@
     sleep(mDisplaySecs);
 
     free_camera_metadata(request);
-    EXPECT_OK(mCamera->cancelStream(streamId));
+
+    for (int i = 0; i < streamCount; ++i) {
+        EXPECT_OK(mCamera->deleteStream(allStreams[i]));
+    }
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, CpuConsumer) {
+    if (HasFatalFailure()) {
+        return;
+    }
+    int streamId = -1;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
+        TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+    EXPECT_EQ(OK, mListener->WaitForEvent());
+    EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
+    /* iterate in a loop submitting requests every frame.
+     *  what kind of requests doesnt really matter, just whatever.
+     */
+
+    // it would probably be better to use CameraMetadata from camera service.
+    camera_metadata_t *request = NULL;
+    EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+        /*out*/&request));
+    EXPECT_NE((void*)NULL, request);
+
+    /*FIXME: dont need this later, at which point the above should become an
+      ASSERT_NE*/
+    if(request == NULL) request = allocate_camera_metadata(10, 100);
+
+    // set the output streams to just this stream ID
+
+    uint8_t allStreams[] = { streamId };
+    camera_metadata_entry_t entry;
+    uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
+    int find = find_camera_metadata_entry(request, tag, &entry);
+    if (find == -ENOENT) {
+        if (add_camera_metadata_entry(request, tag, &allStreams,
+                /*data_count*/1) != OK) {
+            camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
+            ASSERT_OK(append_camera_metadata(tmp, request));
+            free_camera_metadata(request);
+            request = tmp;
+
+            ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
+                /*data_count*/1));
+        }
+    } else {
+        ASSERT_OK(update_camera_metadata_entry(request, entry.index,
+            &allStreams, /*data_count*/1, &entry));
+    }
+
+    EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
+
+    // Consume a couple of frames
+    for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+        EXPECT_EQ(OK, mListener->WaitForEvent());
+        EXPECT_EQ(BUFFER_RECEIVED, mListener->ReadEvent());
+    }
+
+    // Done: clean up
+    free_camera_metadata(request);
+    EXPECT_OK(mCamera->deleteStream(streamId));
     EXPECT_OK(mCamera->exclusiveUnlock());
 }
 
diff --git a/include/camera/ProCamera.h b/include/camera/ProCamera.h
index 9b763a3..4dda533 100644
--- a/include/camera/ProCamera.h
+++ b/include/camera/ProCamera.h
@@ -18,17 +18,20 @@
 #define ANDROID_HARDWARE_PRO_CAMERA_H
 
 #include <utils/Timers.h>
+#include <utils/KeyedVector.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <system/camera.h>
 #include <camera/IProCameraCallbacks.h>
 #include <camera/IProCameraUser.h>
 #include <camera/Camera.h>
+#include <gui/CpuConsumer.h>
 
 struct camera_metadata;
 
 namespace android {
 
-// ref-counted object for callbacks
+// All callbacks on this class are concurrent
+// (they come from separate threads)
 class ProCameraListener : public CameraListener
 {
 public:
@@ -42,6 +45,21 @@
     // Lock free.
     virtual void onTriggerNotify(int32_t msgType, int32_t ext1, int32_t ext2)
                                                                             = 0;
+
+    // OnBufferReceived and OnRequestReceived can come in with any order,
+    // use android.sensor.timestamp and LockedBuffer.timestamp to correlate them
+
+    // TODO: implement in IProCameraCallbacks, ProCamera2Client
+
+    // A new frame buffer has been received for this stream.
+    // -- This callback only fires for createStreamCpu streams
+    // -- The buffer must not be accessed after this function call completes
+    virtual void onBufferReceived(int streamId,
+                                  const CpuConsumer::LockedBuffer& buf) = 0;
+    // A new metadata buffer has been received.
+    // -- Ownership of request passes on to the callee,
+    //    free with free_camera_metadata.
+    virtual void onRequestReceived(camera_metadata* request) = 0;
 };
 
 class ProCamera : public BnProCameraCallbacks, public IBinder::DeathRecipient
@@ -109,22 +127,15 @@
      * Lock free. Service maintains counter of streams.
      */
     status_t requestStream(int streamId);
-    /**
-     * Ask for a stream to be disabled.
-     * Lock free. Service maintains counter of streams.
-     * Errors: BAD_VALUE if unknown stream ID.
-     */
 // TODO: remove requestStream, its useless.
 
-// TODO: rename cancelStream to deleteStream
-// can probably do it with a grep/sed
-
     /**
-      * Ask for a stream to be disabled.
-      * Lock free. Service maintains counter of streams.
+      * Delete a stream.
+      * Lock free.
       * Errors: BAD_VALUE if unknown stream ID.
+      *         PERMISSION_DENIED if the stream wasn't yours
       */
-    status_t cancelStream(int streamId);
+    status_t deleteStream(int streamId);
 
     /**
       * Create a new HW stream, whose sink will be the window.
@@ -145,6 +156,10 @@
                           const sp<IGraphicBufferProducer>& bufferProducer,
                           /*out*/
                           int* streamId);
+    status_t createStreamCpu(int width, int height, int format,
+                          int heapCount,
+                          /*out*/
+                          int* streamId);
 
     // Create a request object from a template.
     status_t createDefaultRequest(int templateId,
@@ -203,6 +218,53 @@
     static  Mutex               mLock;
     static  sp<ICameraService>  mCameraService;
 
+    class ProFrameListener : public CpuConsumer::FrameAvailableListener {
+    public:
+        ProFrameListener(wp<ProCamera> camera, int streamID) {
+            mCamera = camera;
+            mStreamId = streamID;
+        }
+
+    protected:
+        virtual void onFrameAvailable() {
+            sp<ProCamera> c = mCamera.promote();
+            if (c.get() != NULL) {
+                c->onFrameAvailable(mStreamId);
+            }
+        }
+
+    private:
+        wp<ProCamera> mCamera;
+        int mStreamId;
+    };
+    friend class ProFrameListener;
+
+    struct StreamInfo
+    {
+        StreamInfo(int streamId) {
+            this->streamID = streamId;
+            cpuStream = false;
+        }
+
+        StreamInfo() {
+            streamID = -1;
+            cpuStream = false;
+        }
+
+        int  streamID;
+        bool cpuStream;
+        sp<CpuConsumer> cpuConsumer;
+        sp<ProFrameListener> frameAvailableListener;
+        sp<Surface> stc;
+    };
+
+    KeyedVector<int, StreamInfo> mStreams;
+
+
+    void onFrameAvailable(int streamId);
+
+    StreamInfo& getStreamInfo(int streamId);
+
 
 };
 
diff --git a/services/camera/libcameraservice/ProCamera2Client.cpp b/services/camera/libcameraservice/ProCamera2Client.cpp
index aa02f10..f850034 100644
--- a/services/camera/libcameraservice/ProCamera2Client.cpp
+++ b/services/camera/libcameraservice/ProCamera2Client.cpp
@@ -234,6 +234,13 @@
 
     Mutex::Autolock icl(mIProCameraUserLock);
 
+    mDevice->clearStreamingRequest();
+
+    status_t code;
+    if ((code = mDevice->waitUntilDrained()) != OK) {
+        ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__, code);
+    }
+
     return mDevice->deleteStream(streamId);
 }