/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "AImageReaderVendorTest" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { static constexpr int kDummyFenceFd = -1; static constexpr int kCaptureWaitUs = 100 * 1000; static constexpr int kCaptureWaitRetry = 10; static constexpr int kTestImageWidth = 640; static constexpr int kTestImageHeight = 480; static constexpr int kTestImageFormat = AIMAGE_FORMAT_YUV_420_888; using android::hardware::camera::common::V1_0::helper::VendorTagDescriptorCache; using ConfiguredWindows = std::set; class CameraHelper { public: CameraHelper(const char* id, ACameraManager *manager) : mImgReaderAnw(nullptr), mCameraId(id), mCameraManager(manager) {} ~CameraHelper() { closeCamera(); } struct PhysicalImgReaderInfo { const char* physicalCameraId; native_handle_t* anw; }; // Retaining the error code in case the caller needs to analyze it. std::variant initCamera(native_handle_t* imgReaderAnw, const std::vector& physicalImgReaders, bool usePhysicalSettings) { ConfiguredWindows configuredWindows; if (imgReaderAnw == nullptr) { ALOGE("Cannot initialize camera before image reader get initialized."); return -1; } if (mIsCameraReady) { ALOGE("initCamera should only be called once."); return -1; } int ret; mImgReaderAnw = imgReaderAnw; ret = ACameraManager_openCamera(mCameraManager, mCameraId, &mDeviceCb, &mDevice); if (ret != AMEDIA_OK || mDevice == nullptr) { ALOGE("Failed to open camera, ret=%d, mDevice=%p.", ret, mDevice); return ret; } // Create capture session ret = ACaptureSessionOutputContainer_create(&mOutputs); if (ret != AMEDIA_OK) { ALOGE("ACaptureSessionOutputContainer_create failed, ret=%d", ret); return ret; } ret = ACaptureSessionOutput_create(mImgReaderAnw, &mImgReaderOutput); if (ret != AMEDIA_OK) { ALOGE("ACaptureSessionOutput_create failed, ret=%d", ret); return ret; } ret = ACaptureSessionOutputContainer_add(mOutputs, mImgReaderOutput); if (ret != AMEDIA_OK) { ALOGE("ACaptureSessionOutputContainer_add failed, ret=%d", ret); return ret; } configuredWindows.insert(mImgReaderAnw); std::vector idPointerList; std::set physicalStreamMap; 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; } ret = ACameraDevice_isSessionConfigurationSupported(mDevice, mOutputs); if (ret != ACAMERA_OK && ret != ACAMERA_ERROR_UNSUPPORTED_OPERATION) { ALOGW("ACameraDevice_isSessionConfigurationSupported failed, ret=%d camera id %s", ret, mCameraId); ACaptureSessionOutputContainer_remove(mOutputs, sessionOutput); ACaptureSessionOutput_free(sessionOutput); continue; } configuredWindows.insert(physicalStream.anw); // Assume that at most one physical stream per physical camera. mPhysicalCameraIds.push_back(physicalStream.physicalCameraId); idPointerList.push_back(physicalStream.physicalCameraId); physicalStreamMap.insert(physicalStream.anw); mSessionPhysicalOutputs.push_back(sessionOutput); } ACameraIdList cameraIdList; cameraIdList.numCameras = idPointerList.size(); cameraIdList.cameraIds = idPointerList.data(); ret = ACameraDevice_createCaptureSession(mDevice, mOutputs, &mSessionCb, &mSession); if (ret != AMEDIA_OK) { ALOGE("ACameraDevice_createCaptureSession failed, ret=%d", ret); return ret; } // Create capture request if (usePhysicalSettings) { ret = ACameraDevice_createCaptureRequest_withPhysicalIds(mDevice, TEMPLATE_STILL_CAPTURE, &cameraIdList, &mStillRequest); } else { ret = ACameraDevice_createCaptureRequest(mDevice, TEMPLATE_STILL_CAPTURE, &mStillRequest); } if (ret != AMEDIA_OK) { ALOGE("ACameraDevice_createCaptureRequest failed, ret=%d", ret); return ret; } ret = ACameraOutputTarget_create(mImgReaderAnw, &mReqImgReaderOutput); if (ret != AMEDIA_OK) { ALOGE("ACameraOutputTarget_create failed, ret=%d", ret); return ret; } ret = ACaptureRequest_addTarget(mStillRequest, mReqImgReaderOutput); if (ret != AMEDIA_OK) { ALOGE("ACaptureRequest_addTarget failed, ret=%d", ret); return ret; } for (auto& physicalStream : physicalImgReaders) { if (physicalStreamMap.find(physicalStream.anw) == physicalStreamMap.end()) { ALOGI("Skipping physicalStream anw=%p", physicalStream.anw); continue; } ACameraOutputTarget* outputTarget = nullptr; ret = ACameraOutputTarget_create(physicalStream.anw, &outputTarget); if (ret != AMEDIA_OK) { ALOGE("ACameraOutputTarget_create failed, ret=%d", ret); return ret; } ret = ACaptureRequest_addTarget(mStillRequest, outputTarget); if (ret != AMEDIA_OK) { ALOGE("ACaptureRequest_addTarget failed, ret=%d", ret); return ret; } mReqPhysicalOutputs.push_back(outputTarget); } mIsCameraReady = true; return configuredWindows; } bool isCameraReady() { return mIsCameraReady; } void closeCamera() { // Destroy capture request if (mReqImgReaderOutput) { ACameraOutputTarget_free(mReqImgReaderOutput); mReqImgReaderOutput = nullptr; } for (auto& outputTarget : mReqPhysicalOutputs) { ACameraOutputTarget_free(outputTarget); } mReqPhysicalOutputs.clear(); if (mStillRequest) { ACaptureRequest_free(mStillRequest); mStillRequest = nullptr; } // Destroy capture session if (mSession != nullptr) { ACameraCaptureSession_close(mSession); mSession = nullptr; } if (mImgReaderOutput) { ACaptureSessionOutput_free(mImgReaderOutput); mImgReaderOutput = nullptr; } for (auto& extraOutput : mSessionPhysicalOutputs) { ACaptureSessionOutput_free(extraOutput); } mSessionPhysicalOutputs.clear(); if (mOutputs) { ACaptureSessionOutputContainer_free(mOutputs); mOutputs = nullptr; } // Destroy camera device if (mDevice) { ACameraDevice_close(mDevice); mDevice = nullptr; } mIsCameraReady = false; } int takePicture() { int seqId; return ACameraCaptureSession_capture(mSession, &mCaptureCallbacks, 1, &mStillRequest, &seqId); } int takeLogicalCameraPicture() { int seqId; return ACameraCaptureSession_logicalCamera_capture(mSession, &mLogicalCaptureCallbacks, 1, &mStillRequest, &seqId); } bool checkCallbacks(int pictureCount) { std::lock_guard lock(mMutex); if (mCompletedCaptureCallbackCount != pictureCount) { ALOGE("Completed capture callaback count not as expected. expected %d actual %d", pictureCount, mCompletedCaptureCallbackCount); return false; } return true; } private: ACameraDevice_StateCallbacks mDeviceCb{this, nullptr, nullptr}; ACameraCaptureSession_stateCallbacks mSessionCb{ this, nullptr, nullptr, nullptr}; native_handle_t* mImgReaderAnw = nullptr; // not owned by us. // Camera device ACameraDevice* mDevice = nullptr; // Capture session ACaptureSessionOutputContainer* mOutputs = nullptr; ACaptureSessionOutput* mImgReaderOutput = nullptr; std::vector mSessionPhysicalOutputs; ACameraCaptureSession* mSession = nullptr; // Capture request ACaptureRequest* mStillRequest = nullptr; ACameraOutputTarget* mReqImgReaderOutput = nullptr; std::vector mReqPhysicalOutputs; bool mIsCameraReady = false; const char* mCameraId; ACameraManager* mCameraManager; int mCompletedCaptureCallbackCount = 0; std::mutex mMutex; ACameraCaptureSession_captureCallbacks mCaptureCallbacks = { // TODO: Add tests for other callbacks this, // context nullptr, // onCaptureStarted nullptr, // onCaptureProgressed [](void* ctx , ACameraCaptureSession *, ACaptureRequest *, const ACameraMetadata *) { CameraHelper *ch = static_cast(ctx); std::lock_guard lock(ch->mMutex); ch->mCompletedCaptureCallbackCount++; }, nullptr, // onCaptureFailed nullptr, // onCaptureSequenceCompleted nullptr, // onCaptureSequenceAborted nullptr, // onCaptureBufferLost }; std::vector 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(ctx); std::lock_guard 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++; }, [] (void * /*ctx*/, ACameraCaptureSession* /*session*/, ACaptureRequest* /*request*/, ALogicalCameraCaptureFailure* failure) { if (failure->physicalCameraId) { ALOGD("%s: Physical camera id: %s result failure", __FUNCTION__, failure->physicalCameraId); } }, nullptr, // onCaptureSequenceCompleted nullptr, // onCaptureSequenceAborted nullptr, // onCaptureBufferLost }; }; class ImageReaderTestCase { public: ImageReaderTestCase(int32_t width, int32_t height, int32_t format, uint64_t usage, int32_t maxImages, bool async) : mWidth(width), mHeight(height), mFormat(format), mUsage(usage), mMaxImages(maxImages), mAsync(async) {} ~ImageReaderTestCase() { if (mImgReaderAnw) { AImageReader_delete(mImgReader); // No need to call native_handle_t_release on imageReaderAnw } } int initImageReader() { if (mImgReader != nullptr || mImgReaderAnw != nullptr) { ALOGE("Cannot re-initalize image reader, mImgReader=%p, mImgReaderAnw=%p", mImgReader, mImgReaderAnw); return -1; } media_status_t ret = AImageReader_newWithUsage( mWidth, mHeight, mFormat, mUsage, mMaxImages, &mImgReader); if (ret != AMEDIA_OK || mImgReader == nullptr) { ALOGE("Failed to create new AImageReader, ret=%d, mImgReader=%p", ret, mImgReader); return -1; } ret = AImageReader_setImageListener(mImgReader, &mReaderAvailableCb); if (ret != AMEDIA_OK) { ALOGE("Failed to set image available listener, ret=%d.", ret); return ret; } ret = AImageReader_setBufferRemovedListener(mImgReader, &mReaderDetachedCb); if (ret != AMEDIA_OK) { ALOGE("Failed to set buffer detaching listener, ret=%d.", ret); return ret; } ret = AImageReader_getWindowNativeHandle(mImgReader, &mImgReaderAnw); if (ret != AMEDIA_OK || mImgReaderAnw == nullptr) { ALOGE("Failed to get native_handle_t from AImageReader, ret=%d, mImgReaderAnw=%p.", ret, mImgReaderAnw); return -1; } return 0; } native_handle_t* getNativeWindow() { return mImgReaderAnw; } int getAcquiredImageCount() { std::lock_guard lock(mMutex); return mAcquiredImageCount; } void HandleImageAvailable(AImageReader* reader) { std::lock_guard lock(mMutex); AImage* outImage = nullptr; media_status_t ret; // Make sure AImage will be deleted automatically when it goes out of // scope. auto imageDeleter = [this](AImage* img) { if (mAsync) { AImage_deleteAsync(img, kDummyFenceFd); } else { AImage_delete(img); } }; std::unique_ptr img(nullptr, imageDeleter); if (mAsync) { int outFenceFd = 0; // Verity that outFenceFd's value will be changed by // AImageReader_acquireNextImageAsync. ret = AImageReader_acquireNextImageAsync(reader, &outImage, &outFenceFd); if (ret != AMEDIA_OK || outImage == nullptr || outFenceFd == 0) { ALOGE("Failed to acquire image, ret=%d, outIamge=%p, outFenceFd=%d.", ret, outImage, outFenceFd); return; } img.reset(outImage); } else { ret = AImageReader_acquireNextImage(reader, &outImage); if (ret != AMEDIA_OK || outImage == nullptr) { ALOGE("Failed to acquire image, ret=%d, outIamge=%p.", ret, outImage); return; } img.reset(outImage); } AHardwareBuffer* outBuffer = nullptr; ret = AImage_getHardwareBuffer(img.get(), &outBuffer); if (ret != AMEDIA_OK || outBuffer == nullptr) { ALOGE("Faild to get hardware buffer, ret=%d, outBuffer=%p.", ret, outBuffer); return; } // No need to release AHardwareBuffer, since we don't acquire additional // reference to it. AHardwareBuffer_Desc outDesc; AHardwareBuffer_describe(outBuffer, &outDesc); int32_t imageWidth = 0; int32_t imageHeight = 0; int32_t bufferWidth = static_cast(outDesc.width); int32_t bufferHeight = static_cast(outDesc.height); AImage_getWidth(outImage, &imageWidth); AImage_getHeight(outImage, &imageHeight); if (imageWidth != mWidth || imageHeight != mHeight) { ALOGE("Mismatched output image dimension: expected=%dx%d, actual=%dx%d", mWidth, mHeight, imageWidth, imageHeight); return; } if (mFormat == AIMAGE_FORMAT_RGBA_8888 || mFormat == AIMAGE_FORMAT_RGBX_8888 || mFormat == AIMAGE_FORMAT_RGB_888 || mFormat == AIMAGE_FORMAT_RGB_565 || mFormat == AIMAGE_FORMAT_RGBA_FP16 || mFormat == AIMAGE_FORMAT_YUV_420_888 || mFormat == AIMAGE_FORMAT_Y8) { // Check output buffer dimension for certain formats. Don't do this for blob based // formats. if (bufferWidth != mWidth || bufferHeight != mHeight) { ALOGE("Mismatched output buffer dimension: expected=%dx%d, actual=%dx%d", mWidth, mHeight, bufferWidth, bufferHeight); return; } } if ((outDesc.usage & mUsage) != mUsage) { ALOGE("Mismatched output buffer usage: actual (%" PRIu64 "), expected (%" PRIu64 ")", outDesc.usage, mUsage); return; } uint8_t* data = nullptr; int dataLength = 0; ret = AImage_getPlaneData(img.get(), 0, &data, &dataLength); if (mUsage & AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN) { // When we have CPU_READ_OFTEN usage bits, we can lock the image. if (ret != AMEDIA_OK || data == nullptr || dataLength < 0) { ALOGE("Failed to access CPU data, ret=%d, data=%p, dataLength=%d", ret, data, dataLength); return; } } else { if (ret != AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE || data != nullptr || dataLength != 0) { ALOGE("Shouldn't be able to access CPU data, ret=%d, data=%p, dataLength=%d", ret, data, dataLength); return; } } // Only increase mAcquiredImageCount if all checks pass. mAcquiredImageCount++; } static void onImageAvailable(void* obj, AImageReader* reader) { ImageReaderTestCase* thiz = reinterpret_cast(obj); thiz->HandleImageAvailable(reader); } static void onBufferRemoved(void* /*obj*/, AImageReader* /*reader*/, AHardwareBuffer* /*buffer*/) { // No-op, just to check the listener can be set properly. } private: int32_t mWidth; int32_t mHeight; int32_t mFormat; uint64_t mUsage; int32_t mMaxImages; bool mAsync; std::mutex mMutex; int mAcquiredImageCount{0}; AImageReader* mImgReader = nullptr; native_handle_t* mImgReaderAnw = nullptr; AImageReader_ImageListener mReaderAvailableCb{this, onImageAvailable}; AImageReader_BufferRemovedListener mReaderDetachedCb{this, onBufferRemoved}; }; 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; } // TODO: Add more rigorous tests for vendor tags ASSERT_NE(VendorTagDescriptorCache::getGlobalVendorTagCache(), nullptr); 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); ret = testCase.initImageReader(); if (ret < 0) { ALOGE("Unable to initialize ImageReader"); return false; } CameraHelper cameraHelper(id, mCameraManager); std::variant retInit = cameraHelper.initCamera(testCase.getNativeWindow(), {}/*physicalImageReaders*/, false/*usePhysicalSettings*/); int *retp = std::get_if(&retInit); if (retp) { 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."); return true; } for (int i = 0; i < pictureCount; i++) { ret = cameraHelper.takePicture(); if (ret < 0) { ALOGE("Unable to take picture"); return false; } } // Sleep until all capture finished for (int i = 0; i < kCaptureWaitRetry * pictureCount; i++) { usleep(kCaptureWaitUs); if (testCase.getAcquiredImageCount() == pictureCount) { ALOGI("Session take ~%d ms to capture %d images", i * kCaptureWaitUs / 1000, pictureCount); break; } } return testCase.getAcquiredImageCount() == pictureCount && cameraHelper.checkCallbacks(pictureCount); } 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(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; } } } } } 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* 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; } void testLogicalCameraPhysicalStream(bool usePhysicalSettings) { const char* cameraId = nullptr; ACameraMetadata* staticMetadata = nullptr; std::vector 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 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 physicalImgReaderInfo; physicalImgReaderInfo.push_back({physicalCameraIds[0], testCases[1]->getNativeWindow()}); physicalImgReaderInfo.push_back({physicalCameraIds[1], testCases[2]->getNativeWindow()}); std::variant retInit = cameraHelper.initCamera(testCases[0]->getNativeWindow(), physicalImgReaderInfo, usePhysicalSettings); int *retp = std::get_if(&retInit); ASSERT_EQ(retp, nullptr); ConfiguredWindows *configuredWindowsp = std::get_if(&retInit); ASSERT_NE(configuredWindowsp, nullptr); ASSERT_LE(configuredWindowsp->size(), testCases.size()); int 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; } } for(auto &testCase : testCases) { auto it = configuredWindowsp->find(testCase->getNativeWindow()); if (it == configuredWindowsp->end()) { continue; } ALOGI("Testing window %p", testCase->getNativeWindow()); ASSERT_EQ(testCase->getAcquiredImageCount(), pictureCount); } ASSERT_TRUE(cameraHelper.checkCallbacks(pictureCount)); ACameraMetadata_free(staticMetadata); } }; 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); uint32_t namedTag = 0; // Test that ACameraMetadata_getTagFromName works as expected for public tag // names camera_status_t status = ACameraManager_getTagFromName(mCameraManager, cameraId, "android.control.aeMode", &namedTag); ASSERT_EQ(status, ACAMERA_OK); ASSERT_EQ(namedTag, ACAMERA_CONTROL_AE_MODE); ACameraMetadata_free(staticMetadata); if (!isBC) { ALOGW("Camera does not support BACKWARD_COMPATIBLE."); return; } EXPECT_TRUE(testTakePicturesNative(cameraId)); } TEST_F(AImageReaderVendorTest, LogicalCameraPhysicalStream) { testLogicalCameraPhysicalStream(false/*usePhysicalSettings*/); testLogicalCameraPhysicalStream(true/*usePhysicalSettings*/); } } // namespace