Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a140a6ef authored by Igor Murashkin's avatar Igor Murashkin
Browse files

ProCamera: add waitForFrameBuffer/waitForFrameResult blocking calls

Change-Id: I851d41aeecaa15245d5b9d622132e8706d6e292c
parent dcb07d51
Loading
Loading
Loading
Loading
+97 −13
Original line number Diff line number Diff line
@@ -86,12 +86,13 @@ sp<ProCamera> ProCamera::connect(int cameraId)

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 @@ void ProCamera::onResultReceived(int32_t frameId, camera_metadata* result) {
        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::createStream(int width, int height, int format,
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 @@ status_t ProCamera::createStreamCpu(int width, int height, int format,

    cc->setFrameAvailableListener(frameAvailableListener);

    *cpuConsumer = cc;

    return s;
}

@@ -399,26 +418,91 @@ 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);
    if (listener.get() != NULL) {
        if (listener->useOnFrameAvailable()) {
            listener->onFrameAvailable(streamId, stream.cpuConsumer);
            return;
        }
    }

    // Unblock waitForFrame(id) callers
    {
        Mutex::Autolock al(mWaitMutex);
        getStreamInfo(streamId).frameReady = true;
        mWaitCondition.broadcast();
    }
}

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
        }
    }

        listener->onBufferReceived(streamId, buf);
        stat = stream.cpuConsumer->unlockBuffer(buf);
    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: Failed to unlock buffer, error code = %d", __FUNCTION__,
                   stat);
                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) {
+163 −9
Original line number Diff line number Diff line
@@ -207,7 +207,8 @@ protected:
                                  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 @@ protected:
            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 @@ protected:
     * 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 @@ protected:
        request.update(tag, streamIds, count);

        requestTmp = request.release();

        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 @@ TEST_F(ProCameraTest, CpuConsumerSingle) {
    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 @@ TEST_F(ProCameraTest, CpuConsumerDual) {
    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 @@ TEST_F(ProCameraTest, ResultReceiver) {
    // 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 @@ TEST_F(ProCameraTest, ResultReceiver) {
    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());
}





}
}
}
+44 −1
Original line number Diff line number Diff line
@@ -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 @@ public:
      *    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 @@ public:
    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 @@ public:
    // 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 @@ private:
        StreamInfo(int streamId) {
            this->streamID = streamId;
            cpuStream = false;
            frameReady = false;
        }

        StreamInfo() {
@@ -261,10 +299,15 @@ private:
        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);