ProCamera: add waitForFrameBuffer/waitForFrameResult blocking calls

Change-Id: I851d41aeecaa15245d5b9d622132e8706d6e292c
diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp
index c95c4e0..d4a9556 100644
--- a/camera/ProCamera.cpp
+++ b/camera/ProCamera.cpp
@@ -86,12 +86,13 @@
 
 void ProCamera::disconnect()
 {
-    ALOGV("disconnect");
+    ALOGV("%s: disconnect", __FUNCTION__);
     if (mCamera != 0) {
         mCamera->disconnect();
         mCamera->asBinder()->unlinkToDeath(this);
         mCamera = 0;
     }
+    ALOGV("%s: disconnect (done)", __FUNCTION__);
 }
 
 ProCamera::ProCamera()
@@ -208,6 +209,19 @@
         Mutex::Autolock _l(mLock);
         listener = mListener;
     }
+
+    CameraMetadata tmp(result);
+
+    // Unblock waitForFrame(id) callers
+    {
+        Mutex::Autolock al(mWaitMutex);
+        mMetadataReady = true;
+        mLatestMetadata = tmp;
+        mWaitCondition.broadcast();
+    }
+
+    result = tmp.release();
+
     if (listener != NULL) {
         listener->onResultReceived(frameId, result);
     } else {
@@ -323,11 +337,14 @@
 status_t ProCamera::createStreamCpu(int width, int height, int format,
                           int heapCount,
                           /*out*/
+                          sp<CpuConsumer>* cpuConsumer,
                           int* streamId)
 {
     ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
                                                                         format);
 
+    *cpuConsumer = NULL;
+
     sp <IProCameraUser> c = mCamera;
     if (c == 0) return NO_INIT;
 
@@ -357,6 +374,8 @@
 
     cc->setFrameAvailableListener(frameAvailableListener);
 
+    *cpuConsumer = cc;
+
     return s;
 }
 
@@ -399,26 +418,91 @@
     ALOGV("%s: streamId = %d", __FUNCTION__, streamId);
 
     sp<ProCameraListener> listener = mListener;
+    StreamInfo& stream = getStreamInfo(streamId);
+
+    CpuConsumer::LockedBuffer buf;
+
     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);
+        if (listener->useOnFrameAvailable()) {
+            listener->onFrameAvailable(streamId, stream.cpuConsumer);
             return;
         }
+    }
 
-        listener->onBufferReceived(streamId, buf);
-        stat = stream.cpuConsumer->unlockBuffer(buf);
+    // Unblock waitForFrame(id) callers
+    {
+        Mutex::Autolock al(mWaitMutex);
+        getStreamInfo(streamId).frameReady = true;
+        mWaitCondition.broadcast();
+    }
+}
 
-        if (stat != OK) {
-            ALOGE("%s: Failed to unlock buffer, error code = %d", __FUNCTION__,
-                   stat);
+status_t ProCamera::waitForFrameBuffer(int streamId) {
+    status_t stat = BAD_VALUE;
+    Mutex::Autolock al(mWaitMutex);
+
+    StreamInfo& si = getStreamInfo(streamId);
+
+    if (si.frameReady) {
+        si.frameReady = false;
+        return OK;
+    } else {
+        while (true) {
+            stat = mWaitCondition.waitRelative(mWaitMutex,
+                                                mWaitTimeout);
+            if (stat != OK) {
+                ALOGE("%s: Error while waiting for frame buffer: %d",
+                    __FUNCTION__, stat);
+                return stat;
+            }
+
+            if (si.frameReady) {
+                si.frameReady = false;
+                return OK;
+            }
+            // else it was some other stream that got unblocked
         }
     }
+
+    return stat;
+}
+
+status_t ProCamera::waitForFrameMetadata() {
+    status_t stat = BAD_VALUE;
+    Mutex::Autolock al(mWaitMutex);
+
+    if (mMetadataReady) {
+        return OK;
+    } else {
+        while (true) {
+            stat = mWaitCondition.waitRelative(mWaitMutex,
+                                               mWaitTimeout);
+
+            if (stat != OK) {
+                ALOGE("%s: Error while waiting for metadata: %d",
+                        __FUNCTION__, stat);
+                return stat;
+            }
+
+            if (mMetadataReady) {
+                mMetadataReady = false;
+                return OK;
+            }
+            // else it was some other stream or metadata
+        }
+    }
+
+    return stat;
+}
+
+CameraMetadata ProCamera::consumeFrameMetadata() {
+    Mutex::Autolock al(mWaitMutex);
+
+    // Destructive: Subsequent calls return empty metadatas
+    CameraMetadata tmp = mLatestMetadata;
+    mLatestMetadata.release();
+
+    return tmp;
 }
 
 ProCamera::StreamInfo& ProCamera::getStreamInfo(int streamId) {
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
index f1dd48c..33c9179 100644
--- a/camera/tests/ProCameraTests.cpp
+++ b/camera/tests/ProCameraTests.cpp
@@ -207,7 +207,8 @@
                                   const CpuConsumer::LockedBuffer& buf) {
 
         dout << "Buffer received on streamId = " << streamId <<
-                ", dataPtr = " << (void*)buf.data << std::endl;
+                ", dataPtr = " << (void*)buf.data <<
+                ", timestamp = " << buf.timestamp << std::endl;
 
         QueueEvent(BUFFER_RECEIVED);
 
@@ -376,7 +377,7 @@
             return false;
         }
 
-        for (int i = 0; i < count; ++i) {
+        for (size_t i = 0; i < count; ++i) {
             if (array[i] == needle) {
                 return true;
             }
@@ -410,10 +411,10 @@
      * Creating a streaming request for these output streams from a template,
      *  and submit it
      */
-    void createSubmitRequestForStreams(uint8_t* streamIds, size_t count) {
+    void createSubmitRequestForStreams(uint8_t* streamIds, size_t count, int requestCount=-1) {
 
         ASSERT_NE((void*)NULL, streamIds);
-        ASSERT_LT(0, count);
+        ASSERT_LT(0u, count);
 
         camera_metadata_t *requestTmp = NULL;
         EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
@@ -427,7 +428,15 @@
         request.update(tag, streamIds, count);
 
         requestTmp = request.release();
-        EXPECT_OK(mCamera->submitRequest(requestTmp, /*streaming*/true));
+
+        if (requestCount < 0) {
+            EXPECT_OK(mCamera->submitRequest(requestTmp, /*streaming*/true));
+        } else {
+            for (int i = 0; i < requestCount; ++i) {
+                EXPECT_OK(mCamera->submitRequest(requestTmp,
+                                                 /*streaming*/false));
+            }
+        }
         request.acquire(requestTmp);
     }
 
@@ -628,8 +637,9 @@
     mListener->SetEventMask(ProEvent_Mask(BUFFER_RECEIVED));
 
     int streamId = -1;
+    sp<CpuConsumer> consumer;
     EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
-        TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &streamId));
+                TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
     EXPECT_NE(-1, streamId);
 
     EXPECT_OK(mCamera->exclusiveTryLock());
@@ -693,13 +703,14 @@
     mListener->SetEventMask(ProEvent_Mask(BUFFER_RECEIVED));
 
     int streamId = -1;
+    sp<CpuConsumer> consumer;
     EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
-                            TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &streamId));
+                TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
     EXPECT_NE(-1, streamId);
 
     int depthStreamId = -1;
     EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
-                     TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &depthStreamId));
+            TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &depthStreamId));
     EXPECT_NE(-1, depthStreamId);
 
     EXPECT_OK(mCamera->exclusiveTryLock());
@@ -772,8 +783,9 @@
     // need to filter out events at read time
 
     int streamId = -1;
+    sp<CpuConsumer> consumer;
     EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
-                             TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &streamId));
+                TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
     EXPECT_NE(-1, streamId);
 
     EXPECT_OK(mCamera->exclusiveTryLock());
@@ -828,6 +840,148 @@
     EXPECT_OK(mCamera->exclusiveUnlock());
 }
 
+TEST_F(ProCameraTest, WaitForResult) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    int streamId = -1;
+    sp<CpuConsumer> consumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+                 TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+
+    uint8_t streams[] = { streamId };
+    ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1));
+
+    // Consume a couple of results
+    for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+        EXPECT_OK(mCamera->waitForFrameMetadata());
+        CameraMetadata meta = mCamera->consumeFrameMetadata();
+        EXPECT_FALSE(meta.isEmpty());
+    }
+
+    // Done: clean up
+    consumer->abandon(); // since we didn't consume any of the buffers
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, WaitForSingleStreamBuffer) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    int streamId = -1;
+    sp<CpuConsumer> consumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+                  TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+
+    uint8_t streams[] = { streamId };
+    ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
+                                            /*requests*/TEST_CPU_FRAME_COUNT));
+
+    // Consume a couple of results
+    for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+        EXPECT_OK(mCamera->waitForFrameBuffer(streamId));
+
+        CpuConsumer::LockedBuffer buf;
+        EXPECT_OK(consumer->lockNextBuffer(&buf));
+
+        dout << "Buffer synchronously received on streamId = " << streamId <<
+                ", dataPtr = " << (void*)buf.data <<
+                ", timestamp = " << buf.timestamp << std::endl;
+
+        EXPECT_OK(consumer->unlockBuffer(buf));
+    }
+
+    // Done: clean up
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, WaitForDualStreamBuffer) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    const int REQUEST_COUNT = TEST_CPU_FRAME_COUNT * 10;
+
+    // 15 fps
+    int streamId = -1;
+    sp<CpuConsumer> consumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+                 TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    // 30 fps
+    int depthStreamId = -1;
+    sp<CpuConsumer> depthConsumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
+       TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &depthConsumer, &depthStreamId));
+    EXPECT_NE(-1, depthStreamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+
+    uint8_t streams[] = { streamId, depthStreamId };
+    ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/2,
+                                                    /*requests*/REQUEST_COUNT));
+
+    // Consume two frames simultaneously. Unsynchronized by timestamps.
+    for (int i = 0; i < REQUEST_COUNT; ++i) {
+
+        // Get the metadata
+        EXPECT_OK(mCamera->waitForFrameMetadata());
+        CameraMetadata meta = mCamera->consumeFrameMetadata();
+        EXPECT_FALSE(meta.isEmpty());
+
+        // Get the buffers
+
+        EXPECT_OK(mCamera->waitForFrameBuffer(depthStreamId));
+
+        /**
+          * Guaranteed to be able to consume the depth frame,
+          * since we waited on it.
+          */
+        CpuConsumer::LockedBuffer depthBuffer;
+        EXPECT_OK(depthConsumer->lockNextBuffer(&depthBuffer));
+
+        dout << "Depth Buffer synchronously received on streamId = " <<
+                streamId <<
+                ", dataPtr = " << (void*)depthBuffer.data <<
+                ", timestamp = " << depthBuffer.timestamp << std::endl;
+
+        EXPECT_OK(depthConsumer->unlockBuffer(depthBuffer));
+
+
+        /** Consume Greyscale frames if there are any.
+          * There may not be since it runs at half FPS */
+        CpuConsumer::LockedBuffer greyBuffer;
+        while (consumer->lockNextBuffer(&greyBuffer) == OK) {
+
+            dout << "GRAY Buffer synchronously received on streamId = " <<
+                streamId <<
+                ", dataPtr = " << (void*)greyBuffer.data <<
+                ", timestamp = " << greyBuffer.timestamp << std::endl;
+
+            EXPECT_OK(consumer->unlockBuffer(greyBuffer));
+        }
+    }
+
+    // Done: clean up
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+
+
+
+
 }
 }
 }
diff --git a/include/camera/ProCamera.h b/include/camera/ProCamera.h
index 11904f9..f813c1c 100644
--- a/include/camera/ProCamera.h
+++ b/include/camera/ProCamera.h
@@ -24,8 +24,12 @@
 #include <camera/IProCameraCallbacks.h>
 #include <camera/IProCameraUser.h>
 #include <camera/Camera.h>
+#include <camera/CameraMetadata.h>
 #include <gui/CpuConsumer.h>
 
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
 struct camera_metadata;
 
 namespace android {
@@ -62,6 +66,20 @@
       *    free_camera_metadata.
       */
     virtual void onResultReceived(int32_t frameId, camera_metadata* result) = 0;
+
+
+    // A new frame buffer has been received for this stream.
+    // -- This callback only fires for createStreamCpu streams
+    // -- Use buf.timestamp to correlate with metadata's android.sensor.timestamp
+    // -- The buffer should be accessed with CpuConsumer::lockNextBuffer
+    //      and CpuConsumer::unlockBuffer
+    virtual void onFrameAvailable(int streamId,
+                                  const sp<CpuConsumer>& cpuConsumer) {
+    }
+
+    virtual bool useOnFrameAvailable() {
+        return false;
+    }
 };
 
 class ProCamera : public BnProCameraCallbacks, public IBinder::DeathRecipient
@@ -161,6 +179,7 @@
     status_t createStreamCpu(int width, int height, int format,
                           int heapCount,
                           /*out*/
+                          sp<CpuConsumer>* cpuConsumer,
                           int* streamId);
 
     // Create a request object from a template.
@@ -174,6 +193,24 @@
     // Get static camera metadata
     camera_metadata* getCameraInfo(int cameraId);
 
+    // Blocks until a frame is available (CPU streams only)
+    // - Obtain the frame data by calling CpuConsumer::lockNextBuffer
+    // - Release the frame data after use with CpuConsumer::unlockBuffer
+    // Error codes:
+    // -ETIMEDOUT if it took too long to get a frame
+    status_t waitForFrameBuffer(int streamId);
+
+    // Blocks until a metadata result is available
+    // - Obtain the metadata by calling consumeFrameMetadata()
+    // Error codes:
+    // -ETIMEDOUT if it took too long to get a frame
+    status_t waitForFrameMetadata();
+
+    // Get the latest metadata. This is destructive.
+    // - Calling this repeatedly will produce empty metadata objects.
+    // - Use waitForFrameMetadata to sync until new data is available.
+    CameraMetadata consumeFrameMetadata();
+
     sp<IProCameraUser>         remote();
 
 protected:
@@ -249,6 +286,7 @@
         StreamInfo(int streamId) {
             this->streamID = streamId;
             cpuStream = false;
+            frameReady = false;
         }
 
         StreamInfo() {
@@ -261,10 +299,15 @@
         sp<CpuConsumer> cpuConsumer;
         sp<ProFrameListener> frameAvailableListener;
         sp<Surface> stc;
+        bool frameReady;
     };
 
+    Condition mWaitCondition;
+    Mutex     mWaitMutex;
+    static const nsecs_t mWaitTimeout = 1000000000; // 1sec
     KeyedVector<int, StreamInfo> mStreams;
-
+    bool mMetadataReady;
+    CameraMetadata mLatestMetadata;
 
     void onFrameAvailable(int streamId);