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

Commit abf2929e authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Camera: Add vndk test for logical camera"

parents f25675d3 33885736
Loading
Loading
Loading
Loading
+348 −120
Original line number Diff line number Diff line
@@ -55,38 +55,27 @@ static constexpr int kTestImageFormat = AIMAGE_FORMAT_YUV_420_888;

class CameraHelper {
   public:
    CameraHelper(native_handle_t* imgReaderAnw) : mImgReaderAnw(imgReaderAnw) {}
    CameraHelper(const char* id, ACameraManager *manager) :
            mImgReaderAnw(nullptr), mCameraId(id), mCameraManager(manager) {}
    ~CameraHelper() { closeCamera(); }

    int initCamera() {
        if (mImgReaderAnw == nullptr) {
    struct PhysicalImgReaderInfo {
        const char* physicalCameraId;
        native_handle_t* anw;
    };
    int initCamera(native_handle_t* imgReaderAnw,
            const std::vector<PhysicalImgReaderInfo>& physicalImgReaders) {
        if (imgReaderAnw == nullptr) {
            ALOGE("Cannot initialize camera before image reader get initialized.");
            return -1;
        }
        int ret;

        mCameraManager = ACameraManager_create();
        if (mCameraManager == nullptr) {
            ALOGE("Failed to create ACameraManager.");
        if (mIsCameraReady) {
            ALOGE("initCamera should only be called once.");
            return -1;
        }

        ret = ACameraManager_getCameraIdList(mCameraManager, &mCameraIdList);
        if (ret != AMEDIA_OK) {
            ALOGE("Failed to get cameraIdList: ret=%d", ret);
            return ret;
        }
        if (mCameraIdList->numCameras < 1) {
            ALOGW("Device has no camera on board.");
            return 0;
        }

        // We always use the first camera.
        mCameraId = mCameraIdList->cameraIds[0];
        if (mCameraId == nullptr) {
            ALOGE("Failed to get cameraId.");
            return -1;
        }
        int ret;
        mImgReaderAnw = imgReaderAnw;

        ret = ACameraManager_openCamera(mCameraManager, mCameraId, &mDeviceCb, &mDevice);
        if (ret != AMEDIA_OK || mDevice == nullptr) {
@@ -94,18 +83,6 @@ class CameraHelper {
            return -1;
        }

        ret = ACameraManager_getCameraCharacteristics(mCameraManager, mCameraId, &mCameraMetadata);
        if (ret != ACAMERA_OK || mCameraMetadata == nullptr) {
            ALOGE("Get camera %s characteristics failure. ret %d, metadata %p", mCameraId, ret,
                  mCameraMetadata);
            return -1;
        }

        if (!isCapabilitySupported(ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)) {
            ALOGW("Camera does not support BACKWARD_COMPATIBLE.");
            return 0;
        }

        // Create capture session
        ret = ACaptureSessionOutputContainer_create(&mOutputs);
        if (ret != AMEDIA_OK) {
@@ -122,6 +99,25 @@ class CameraHelper {
            ALOGE("ACaptureSessionOutputContainer_add failed, ret=%d", ret);
            return ret;
        }

        for (auto& physicalStream : physicalImgReaders) {
            ACaptureSessionOutput* sessionOutput = nullptr;
            ret = ACaptureSessionPhysicalOutput_create(physicalStream.anw,
                    physicalStream.physicalCameraId, &sessionOutput);
            if (ret != ACAMERA_OK) {
                ALOGE("ACaptureSessionPhysicalOutput_create failed, ret=%d", ret);
                return ret;
            }
            ret = ACaptureSessionOutputContainer_add(mOutputs, sessionOutput);
            if (ret != AMEDIA_OK) {
                ALOGE("ACaptureSessionOutputContainer_add failed, ret=%d", ret);
                return ret;
            }
            mExtraOutputs.push_back(sessionOutput);
            // Assume that at most one physical stream per physical camera.
            mPhysicalCameraIds.push_back(physicalStream.physicalCameraId);
        }

        ret = ACameraDevice_createCaptureSession(mDevice, mOutputs, &mSessionCb, &mSession);
        if (ret != AMEDIA_OK) {
            ALOGE("ACameraDevice_createCaptureSession failed, ret=%d", ret);
@@ -145,22 +141,26 @@ class CameraHelper {
            return ret;
        }

        mIsCameraReady = true;
        return 0;
        for (auto& physicalStream : physicalImgReaders) {
            ACameraOutputTarget* outputTarget = nullptr;
            ret = ACameraOutputTarget_create(physicalStream.anw, &outputTarget);
            if (ret != AMEDIA_OK) {
                ALOGE("ACameraOutputTarget_create failed, ret=%d", ret);
                return ret;
            }

    bool isCapabilitySupported(acamera_metadata_enum_android_request_available_capabilities_t cap) {
        ACameraMetadata_const_entry entry;
        ACameraMetadata_getConstEntry(
                mCameraMetadata, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry);
        for (uint32_t i = 0; i < entry.count; i++) {
            if (entry.data.u8[i] == cap) {
                return true;
            ret = ACaptureRequest_addTarget(mStillRequest, outputTarget);
            if (ret != AMEDIA_OK) {
                ALOGE("ACaptureRequest_addTarget failed, ret=%d", ret);
                return ret;
            }
            mReqExtraOutputs.push_back(outputTarget);
        }
        return false;

        mIsCameraReady = true;
        return 0;
    }


    bool isCameraReady() { return mIsCameraReady; }

    void closeCamera() {
@@ -169,6 +169,10 @@ class CameraHelper {
            ACameraOutputTarget_free(mReqImgReaderOutput);
            mReqImgReaderOutput = nullptr;
        }
        for (auto& outputTarget : mReqExtraOutputs) {
            ACameraOutputTarget_free(outputTarget);
        }
        mReqExtraOutputs.clear();
        if (mStillRequest) {
            ACaptureRequest_free(mStillRequest);
            mStillRequest = nullptr;
@@ -182,6 +186,10 @@ class CameraHelper {
            ACaptureSessionOutput_free(mImgReaderOutput);
            mImgReaderOutput = nullptr;
        }
        for (auto& extraOutput : mExtraOutputs) {
            ACaptureSessionOutput_free(extraOutput);
        }
        mExtraOutputs.clear();
        if (mOutputs) {
            ACaptureSessionOutputContainer_free(mOutputs);
            mOutputs = nullptr;
@@ -191,19 +199,6 @@ class CameraHelper {
            ACameraDevice_close(mDevice);
            mDevice = nullptr;
        }
        if (mCameraMetadata) {
          ACameraMetadata_free(mCameraMetadata);
          mCameraMetadata = nullptr;
        }
        // Destroy camera manager
        if (mCameraIdList) {
            ACameraManager_deleteCameraIdList(mCameraIdList);
            mCameraIdList = nullptr;
        }
        if (mCameraManager) {
            ACameraManager_delete(mCameraManager);
            mCameraManager = nullptr;
        }
        mIsCameraReady = false;
    }

@@ -213,6 +208,12 @@ class CameraHelper {
                                             &seqId);
    }

    int takeLogicalCameraPicture() {
        int seqId;
        return ACameraCaptureSession_logicalCamera_capture(mSession, &mLogicalCaptureCallbacks,
                1, &mStillRequest, &seqId);
    }

    bool checkCallbacks(int pictureCount) {
        std::lock_guard<std::mutex> lock(mMutex);
        if (mCompletedCaptureCallbackCount != pictureCount) {
@@ -241,22 +242,22 @@ class CameraHelper {

    native_handle_t* mImgReaderAnw = nullptr;  // not owned by us.

    // Camera manager
    ACameraManager* mCameraManager = nullptr;
    ACameraIdList* mCameraIdList = nullptr;
    // Camera device
    ACameraMetadata* mCameraMetadata = nullptr;
    ACameraDevice* mDevice = nullptr;
    // Capture session
    ACaptureSessionOutputContainer* mOutputs = nullptr;
    ACaptureSessionOutput* mImgReaderOutput = nullptr;
    std::vector<ACaptureSessionOutput*> mExtraOutputs;

    ACameraCaptureSession* mSession = nullptr;
    // Capture request
    ACaptureRequest* mStillRequest = nullptr;
    ACameraOutputTarget* mReqImgReaderOutput = nullptr;
    std::vector<ACameraOutputTarget*> mReqExtraOutputs;

    bool mIsCameraReady = false;
    const char* mCameraId;
    ACameraManager* mCameraManager;
    int mCompletedCaptureCallbackCount = 0;
    std::mutex mMutex;
    ACameraCaptureSession_captureCallbacks mCaptureCallbacks = {
@@ -264,7 +265,6 @@ class CameraHelper {
        this, // context
        nullptr, // onCaptureStarted
        nullptr, // onCaptureProgressed
        // onCaptureCompleted, called serially, so no lock needed.
        [](void* ctx , ACameraCaptureSession *, ACaptureRequest *,
                                          const ACameraMetadata *) {
            CameraHelper *ch = static_cast<CameraHelper *>(ctx);
@@ -277,6 +277,42 @@ class CameraHelper {
        nullptr, // onCaptureBufferLost
    };

    std::vector<std::string> mPhysicalCameraIds;
    ACameraCaptureSession_logicalCamera_captureCallbacks mLogicalCaptureCallbacks = {
        // TODO: Add tests for other callbacks
        this, // context
        nullptr, // onCaptureStarted
        nullptr, // onCaptureProgressed
        [](void* ctx , ACameraCaptureSession *, ACaptureRequest *,
                const ACameraMetadata *, size_t physicalResultCount,
                const char** physicalCameraIds, const ACameraMetadata** physicalResults) {
            CameraHelper *ch = static_cast<CameraHelper *>(ctx);
            std::lock_guard<std::mutex> lock(ch->mMutex);
            ASSERT_EQ(physicalResultCount, ch->mPhysicalCameraIds.size());
            for (size_t i = 0; i < physicalResultCount; i++) {
                ASSERT_TRUE(physicalCameraIds[i] != nullptr);
                ASSERT_TRUE(physicalResults[i] != nullptr);
                ASSERT_NE(std::find(ch->mPhysicalCameraIds.begin(),
                        ch->mPhysicalCameraIds.end(), physicalCameraIds[i]),
                        ch->mPhysicalCameraIds.end());

                // Verify frameNumber and sensorTimestamp exist in physical
                // result metadata
                ACameraMetadata_const_entry entry;
                ACameraMetadata_getConstEntry(
                        physicalResults[i], ACAMERA_SYNC_FRAME_NUMBER, &entry);
                ASSERT_EQ(entry.count, 1);
                ACameraMetadata_getConstEntry(
                        physicalResults[i], ACAMERA_SENSOR_TIMESTAMP, &entry);
                ASSERT_EQ(entry.count, 1);
            }
            ch->mCompletedCaptureCallbackCount++;
        },
        nullptr, // onCaptureFailed
        nullptr, // onCaptureSequenceCompleted
        nullptr, // onCaptureSequenceAborted
        nullptr, // onCaptureBufferLost
    };
};

class ImageReaderTestCase {
@@ -476,8 +512,42 @@ class ImageReaderTestCase {
    AImageReader_BufferRemovedListener mReaderDetachedCb{this, onBufferRemoved};
};

bool takePictures(uint64_t readerUsage, int readerMaxImages, bool readerAsync, int pictureCount) {

class AImageReaderVendorTest : public ::testing::Test {
  public:
    void SetUp() override {
        mCameraManager = ACameraManager_create();
        if (mCameraManager == nullptr) {
            ALOGE("Failed to create ACameraManager.");
            return;
        }

        camera_status_t ret = ACameraManager_getCameraIdList(mCameraManager, &mCameraIdList);
        if (ret != ACAMERA_OK) {
            ALOGE("Failed to get cameraIdList: ret=%d", ret);
            return;
        }
        if (mCameraIdList->numCameras < 1) {
            ALOGW("Device has no camera on board.");
            return;
        }
    }
    void TearDown() override {
        // Destroy camera manager
        if (mCameraIdList) {
            ACameraManager_deleteCameraIdList(mCameraIdList);
            mCameraIdList = nullptr;
        }
        if (mCameraManager) {
            ACameraManager_delete(mCameraManager);
            mCameraManager = nullptr;
        }
    }

    bool takePictures(const char* id, uint64_t readerUsage, int readerMaxImages,
            bool readerAsync, int pictureCount) {
        int ret = 0;

        ImageReaderTestCase testCase(
                kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage, readerMaxImages,
                readerAsync);
@@ -487,17 +557,17 @@ bool takePictures(uint64_t readerUsage, int readerMaxImages, bool readerAsync, i
            return false;
        }

    CameraHelper cameraHelper(testCase.getNativeWindow());
    ret = cameraHelper.initCamera();
        CameraHelper cameraHelper(id, mCameraManager);
        ret = cameraHelper.initCamera(testCase.getNativeWindow(), {});
        if (ret < 0) {
            ALOGE("Unable to initialize camera helper");
            return false;
        }

        if (!cameraHelper.isCameraReady()) {
        ALOGW("Camera is not ready after successful initialization. It's either due to camera on "
              "board lacks BACKWARDS_COMPATIBLE capability or the device does not have camera on "
              "board.");
            ALOGW("Camera is not ready after successful initialization. It's either due to camera "
                  "on board lacks BACKWARDS_COMPATIBLE capability or the device does not have "
                  "camera on board.");
            return true;
        }

@@ -522,25 +592,16 @@ bool takePictures(uint64_t readerUsage, int readerMaxImages, bool readerAsync, i
                cameraHelper.checkCallbacks(pictureCount);
    }

class AImageReaderWindowHandleTest : public ::testing::Test {
   public:
    void SetUp() override {
    }
    void TearDown() override {

    }

};

bool testTakePicturesNative() {
    bool testTakePicturesNative(const char* id) {
        for (auto& readerUsage :
             {AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}) {
            for (auto& readerMaxImages : {1, 4, 8}) {
                for (auto& readerAsync : {true, false}) {
                    for (auto& pictureCount : {1, 4, 8}) {
                    if (!takePictures(readerUsage, readerMaxImages, readerAsync, pictureCount)) {
                        ALOGE("Test takePictures failed for test case usage=%" PRIu64 ", maxImages=%d, "
                              "async=%d, pictureCount=%d",
                        if (!takePictures(id, readerUsage, readerMaxImages,
                                readerAsync, pictureCount)) {
                            ALOGE("Test takePictures failed for test case usage=%" PRIu64
                                  ", maxImages=%d, async=%d, pictureCount=%d",
                                  readerUsage, readerMaxImages, readerAsync, pictureCount);
                            return false;
                        }
@@ -551,9 +612,176 @@ bool testTakePicturesNative() {
        return true;
    }

    // Camera manager
    ACameraManager* mCameraManager = nullptr;
    ACameraIdList* mCameraIdList = nullptr;

    bool isCapabilitySupported(ACameraMetadata* staticInfo,
            acamera_metadata_enum_android_request_available_capabilities_t cap) {
        ACameraMetadata_const_entry entry;
        ACameraMetadata_getConstEntry(
                staticInfo, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry);
        for (uint32_t i = 0; i < entry.count; i++) {
            if (entry.data.u8[i] == cap) {
                return true;
            }
        }
        return false;
    }

    bool isSizeSupportedForFormat(ACameraMetadata* staticInfo,
            int32_t format, int32_t width, int32_t height) {
        ACameraMetadata_const_entry entry;
        ACameraMetadata_getConstEntry(staticInfo,
                ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry);
        for (uint32_t i = 0; i < entry.count; i += 4) {
            if (entry.data.i32[i] == format &&
                    entry.data.i32[i+3] == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
                    entry.data.i32[i+1] == width &&
                    entry.data.i32[i+2] == height) {
                return true;
            }
        }
        return false;
    }

    void findCandidateLogicalCamera(const char **cameraId,
            ACameraMetadata** staticMetadata,
            std::vector<const char*>* candidatePhysicalIds) {
        // Find first available logical camera
        for (int i = 0; i < mCameraIdList->numCameras; i++) {
            camera_status_t ret;
            ret = ACameraManager_getCameraCharacteristics(
                    mCameraManager, mCameraIdList->cameraIds[i], staticMetadata);
            ASSERT_EQ(ret, ACAMERA_OK);
            ASSERT_NE(*staticMetadata, nullptr);

            if (!isCapabilitySupported(*staticMetadata,
                    ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
                ACameraMetadata_free(*staticMetadata);
                *staticMetadata = nullptr;
                continue;
            }

            // Check returned physical camera Ids are valid
            size_t physicalCameraIdCnt = 0;
            const char*const* physicalCameraIds = nullptr;
            bool isLogicalCamera = ACameraMetadata_isLogicalMultiCamera(*staticMetadata,
                    &physicalCameraIdCnt, &physicalCameraIds);
            ASSERT_TRUE(isLogicalCamera);
            ASSERT_GE(physicalCameraIdCnt, 2);
            ACameraMetadata* physicalCameraMetadata = nullptr;
            candidatePhysicalIds->clear();
            for (size_t j = 0; j < physicalCameraIdCnt && candidatePhysicalIds->size() < 2; j++) {
                ASSERT_GT(strlen(physicalCameraIds[j]), 0);
                ret = ACameraManager_getCameraCharacteristics(
                        mCameraManager, physicalCameraIds[j], &physicalCameraMetadata);
                ASSERT_EQ(ret, ACAMERA_OK);
                ASSERT_NE(physicalCameraMetadata, nullptr);

                if (isSizeSupportedForFormat(physicalCameraMetadata, kTestImageFormat,
                        kTestImageWidth, kTestImageHeight)) {
                    candidatePhysicalIds->push_back(physicalCameraIds[j]);
                }
                ACameraMetadata_free(physicalCameraMetadata);
            }
            if (candidatePhysicalIds->size() == 2) {
                *cameraId = mCameraIdList->cameraIds[i];
                return;
            } else {
                ACameraMetadata_free(*staticMetadata);
                *staticMetadata = nullptr;
            }
        }
        *cameraId = nullptr;
        return;
    }
};

TEST_F(AImageReaderVendorTest, CreateWindowNativeHandle) {
    // We always use the first camera.
    const char* cameraId = mCameraIdList->cameraIds[0];
    ASSERT_TRUE(cameraId != nullptr);

    ACameraMetadata* staticMetadata = nullptr;
    camera_status_t ret = ACameraManager_getCameraCharacteristics(
            mCameraManager, cameraId, &staticMetadata);
    ASSERT_EQ(ret, ACAMERA_OK);
    ASSERT_NE(staticMetadata, nullptr);

    bool isBC = isCapabilitySupported(staticMetadata,
            ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);

    ACameraMetadata_free(staticMetadata);

    if (!isBC) {
        ALOGW("Camera does not support BACKWARD_COMPATIBLE.");
        return;
    }

    EXPECT_TRUE(testTakePicturesNative(cameraId));
}

TEST_F(AImageReaderVendorTest, LogicalCameraPhysicalStream) {
    const char* cameraId = nullptr;
    ACameraMetadata* staticMetadata = nullptr;
    std::vector<const char*> physicalCameraIds;

    findCandidateLogicalCamera(&cameraId, &staticMetadata, &physicalCameraIds);
    if (cameraId == nullptr) {
        // Couldn't find logical camera to test
        return;
    }

    // Test streaming the logical multi-camera
    uint64_t readerUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
    int32_t readerMaxImages = 8;
    bool readerAsync = false;
    const int pictureCount = 6;
    std::vector<ImageReaderTestCase*> testCases;
    for (size_t i = 0; i < 3; i++) {
        ImageReaderTestCase* testCase = new ImageReaderTestCase(
                kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage, readerMaxImages,
                readerAsync);
        ASSERT_EQ(testCase->initImageReader(), 0);
        testCases.push_back(testCase);
    }

    CameraHelper cameraHelper(cameraId, mCameraManager);
    std::vector<CameraHelper::PhysicalImgReaderInfo> physicalImgReaderInfo;
    physicalImgReaderInfo.push_back({physicalCameraIds[0], testCases[1]->getNativeWindow()});
    physicalImgReaderInfo.push_back({physicalCameraIds[1], testCases[2]->getNativeWindow()});

    int ret = cameraHelper.initCamera(testCases[0]->getNativeWindow(), physicalImgReaderInfo);
    ASSERT_EQ(ret, 0);

    if (!cameraHelper.isCameraReady()) {
        ALOGW("Camera is not ready after successful initialization. It's either due to camera on "
              "board lacks BACKWARDS_COMPATIBLE capability or the device does not have camera on "
              "board.");
        return;
    }

    for (int i = 0; i < pictureCount; i++) {
        ret = cameraHelper.takeLogicalCameraPicture();
        ASSERT_EQ(ret, 0);
    }

    // Sleep until all capture finished
    for (int i = 0; i < kCaptureWaitRetry * pictureCount; i++) {
        usleep(kCaptureWaitUs);
        if (testCases[0]->getAcquiredImageCount() == pictureCount) {
            ALOGI("Session take ~%d ms to capture %d images", i * kCaptureWaitUs / 1000,
                  pictureCount);
            break;
        }
    }
    ASSERT_EQ(testCases[0]->getAcquiredImageCount(), pictureCount);
    ASSERT_EQ(testCases[1]->getAcquiredImageCount(), pictureCount);
    ASSERT_EQ(testCases[2]->getAcquiredImageCount(), pictureCount);
    ASSERT_TRUE(cameraHelper.checkCallbacks(pictureCount));

TEST_F(AImageReaderWindowHandleTest, CreateWindowNativeHandle) {
    EXPECT_TRUE(testTakePicturesNative());
    ACameraMetadata_free(staticMetadata);
}

}  // namespace