blob: 7ab0124be10f1270bea4ae8e165916c775940bab [file] [log] [blame]
/*
* 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 <stdint.h>
#include <unistd.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <mutex>
#include <string>
#include <variant>
#include <vector>
#include <stdio.h>
#include <stdio.h>
#include <stdio.h>
#include <android/log.h>
#include <camera/NdkCameraError.h>
#include <camera/NdkCameraManager.h>
#include <camera/NdkCameraDevice.h>
#include <camera/NdkCameraCaptureSession.h>
#include <media/NdkImage.h>
#include <media/NdkImageReader.h>
#include <cutils/native_handle.h>
#include <VendorTagDescriptor.h>
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<native_handle_t *>;
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<int, ConfiguredWindows> initCamera(native_handle_t* imgReaderAnw,
const std::vector<PhysicalImgReaderInfo>& 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<const char*> idPointerList;
std::set<const native_handle_t*> 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<std::mutex> lock(mMutex);
if (mCompletedCaptureCallbackCount != pictureCount) {
ALOGE("Completed capture callaback count not as expected. expected %d actual %d",
pictureCount, mCompletedCaptureCallbackCount);
return false;
}
return true;
}
static void onDeviceDisconnected(void* /*obj*/, ACameraDevice* /*device*/) {}
static void onDeviceError(void* /*obj*/, ACameraDevice* /*device*/, int /*errorCode*/) {}
static void onSessionClosed(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
static void onSessionReady(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
static void onSessionActive(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
private:
ACameraDevice_StateCallbacks mDeviceCb{this, onDeviceDisconnected,
onDeviceError};
ACameraCaptureSession_stateCallbacks mSessionCb{
this, onSessionClosed, onSessionReady, onSessionActive};
native_handle_t* mImgReaderAnw = nullptr; // not owned by us.
// Camera device
ACameraDevice* mDevice = nullptr;
// Capture session
ACaptureSessionOutputContainer* mOutputs = nullptr;
ACaptureSessionOutput* mImgReaderOutput = nullptr;
std::vector<ACaptureSessionOutput*> mSessionPhysicalOutputs;
ACameraCaptureSession* mSession = nullptr;
// Capture request
ACaptureRequest* mStillRequest = nullptr;
ACameraOutputTarget* mReqImgReaderOutput = nullptr;
std::vector<ACameraOutputTarget*> 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<CameraHelper *>(ctx);
std::lock_guard<std::mutex> lock(ch->mMutex);
ch->mCompletedCaptureCallbackCount++;
},
nullptr, // onCaptureFailed
nullptr, // onCaptureSequenceCompleted
nullptr, // onCaptureSequenceAborted
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++;
},
[] (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<std::mutex> lock(mMutex);
return mAcquiredImageCount;
}
void HandleImageAvailable(AImageReader* reader) {
std::lock_guard<std::mutex> 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<AImage, decltype(imageDeleter)> 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<int32_t>(outDesc.width);
int32_t bufferHeight = static_cast<int32_t>(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<ImageReaderTestCase*>(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<int, ConfiguredWindows> retInit =
cameraHelper.initCamera(testCase.getNativeWindow(), {}/*physicalImageReaders*/,
false/*usePhysicalSettings*/);
int *retp = std::get_if<int>(&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<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;
}
void testLogicalCameraPhysicalStream(bool usePhysicalSettings) {
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()});
std::variant<int, ConfiguredWindows> retInit =
cameraHelper.initCamera(testCases[0]->getNativeWindow(), physicalImgReaderInfo,
usePhysicalSettings);
int *retp = std::get_if<int>(&retInit);
ASSERT_EQ(retp, nullptr);
ConfiguredWindows *configuredWindowsp = std::get_if<ConfiguredWindows>(&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