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

Commit 5835cc46 authored by Igor Murashkin's avatar Igor Murashkin
Browse files

Camera: ProCamera - add createStreamCpu and unit test

Change-Id: I468172dbfdd78510b273bf9d119c950cbeda7ea3
parent 76f8b439
Loading
Loading
Loading
Loading
+88 −23
Original line number Diff line number Diff line
@@ -246,19 +246,16 @@ status_t ProCamera::cancelRequest(int requestId)
    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,10 +272,7 @@ status_t ProCamera::createStream(int width, int height, int format,
        return BAD_VALUE;
    }

    sp <IProCameraUser> c = mCamera;
    if (c == 0) return NO_INIT;

    return c->createStream(width, height, format, surface->getIGraphicBufferProducer(),
    return createStream(width, height, format, surface->getIGraphicBufferProducer(),
                        streamId);
}

@@ -286,27 +280,68 @@ 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) {
        return BAD_VALUE;
    }

    if (bufferProducer != 0) {
    sp <IProCameraUser> c = mCamera;
        if (c == 0) return NO_INIT;
    status_t stat = c->createStream(width, height, format, bufferProducer,
                                    streamId);

        return c->createStream(width, height, format, bufferProducer, streamId);
    }
    else {
        *streamId = -1;
        return BAD_VALUE;
    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 @@ status_t ProCamera::createDefaultRequest(int templateId,
    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
+147 −28
Original line number Diff line number Diff line
@@ -41,7 +41,12 @@ namespace client {
#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 @@ namespace client {

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 @@ public:
        {
            Mutex::Autolock al(mListenerMutex);

            if (mLockEventList.size() > 0) {
            if (mProEventList.size() > 0) {
                return OK;
            }
        }
@@ -105,35 +111,35 @@ public:
    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 @@ protected:
             << " " << 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 @@ protected:
    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 @@ protected:
    sp<SurfaceComposerClient> mComposerClient;
    sp<SurfaceControl> mSurfaceControl;

    sp<SurfaceComposerClient> mDepthComposerClient;
    sp<SurfaceControl> mDepthSurfaceControl;

    int getSurfaceWidth() {
        return 512;
    }
@@ -233,6 +256,8 @@ protected:
                getSurfaceWidth(), getSurfaceHeight(),
                PIXEL_FORMAT_RGB_888, 0);

        mSurfaceControl->setPosition(640, 0);

        ASSERT_TRUE(mSurfaceControl != NULL);
        ASSERT_TRUE(mSurfaceControl->isValid());

@@ -247,6 +272,31 @@ protected:
        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 @@ TEST_F(ProCameraTest, StreamingImage) {
        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 @@ TEST_F(ProCameraTest, StreamingImage) {
    // 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 @@ TEST_F(ProCameraTest, StreamingImage) {
    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());
}

+74 −12
Original line number Diff line number Diff line
@@ -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 @@ public:
    // 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 @@ public:
     * 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 @@ public:
                          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 @@ private:
    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);


};

+7 −0
Original line number Diff line number Diff line
@@ -234,6 +234,13 @@ status_t ProCamera2Client::cancelStream(int streamId) {

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