diff --git a/camera/device/3.2/default/CameraDeviceSession.cpp b/camera/device/3.2/default/CameraDeviceSession.cpp index 631404e79c..31b47392e8 100644 --- a/camera/device/3.2/default/CameraDeviceSession.cpp +++ b/camera/device/3.2/default/CameraDeviceSession.cpp @@ -333,11 +333,10 @@ void CameraDeviceSession::ResultBatcher::setResultMetadataQueue( mResultMetadataQueue = q; } -void CameraDeviceSession::ResultBatcher::registerBatch( - const hidl_vec& requests) { +void CameraDeviceSession::ResultBatcher::registerBatch(uint32_t frameNumber, uint32_t batchSize) { auto batch = std::make_shared(); - batch->mFirstFrame = requests[0].frameNumber; - batch->mBatchSize = requests.size(); + batch->mFirstFrame = frameNumber; + batch->mBatchSize = batchSize; batch->mLastFrame = batch->mFirstFrame + batch->mBatchSize - 1; batch->mNumPartialResults = mNumPartialResults; for (int id : mStreamsToBatch) { @@ -1010,7 +1009,7 @@ Return CameraDeviceSession::processCaptureRequest( } if (s == Status::OK && requests.size() > 1) { - mResultBatcher.registerBatch(requests); + mResultBatcher.registerBatch(requests[0].frameNumber, requests.size()); } _hidl_cb(s, numRequestProcessed); @@ -1111,6 +1110,7 @@ Status CameraDeviceSession::processOneCaptureRequest(const CaptureRequest& reque halRequest.settings = settingsOverride.getAndLock(); } } + halRequest.num_physcam_settings = 0; ATRACE_ASYNC_BEGIN("frame capture", request.frameNumber); ATRACE_BEGIN("camera3->process_capture_request"); diff --git a/camera/device/3.2/default/CameraDeviceSession.h b/camera/device/3.2/default/CameraDeviceSession.h index c5a63c8929..0048ef49b3 100644 --- a/camera/device/3.2/default/CameraDeviceSession.h +++ b/camera/device/3.2/default/CameraDeviceSession.h @@ -184,7 +184,7 @@ protected: void setBatchedStreams(const std::vector& streamsToBatch); void setResultMetadataQueue(std::shared_ptr q); - void registerBatch(const hidl_vec& requests); + void registerBatch(uint32_t frameNumber, uint32_t batchSize); void notify(NotifyMsg& msg); void processCaptureResult(CaptureResult& result); diff --git a/camera/device/3.4/ICameraDeviceSession.hal b/camera/device/3.4/ICameraDeviceSession.hal index 71a4b34c3e..4ce749d7aa 100644 --- a/camera/device/3.4/ICameraDeviceSession.hal +++ b/camera/device/3.4/ICameraDeviceSession.hal @@ -20,7 +20,6 @@ import android.hardware.camera.common@1.0::Status; import @3.3::ICameraDeviceSession; import @3.3::HalStreamConfiguration; import @3.2::BufferCache; -import @3.2::CaptureRequest; /** * Camera device active session interface. @@ -72,4 +71,38 @@ interface ICameraDeviceSession extends @3.3::ICameraDeviceSession { configureStreams_3_4(@3.4::StreamConfiguration requestedConfiguration) generates (Status status, @3.4::HalStreamConfiguration halConfiguration); + + /** + * processCaptureRequest_3_4: + * + * Identical to @3.2::ICameraDeviceSession.processCaptureRequest, except that: + * + * - The capture request can include individual settings for physical camera devices + * backing a logical multi-camera. + * + * @return status Status code for the operation, one of: + * OK: + * On a successful start to processing the capture request + * ILLEGAL_ARGUMENT: + * If the input is malformed (the settings are empty when not + * allowed, the physical camera settings are invalid, there are 0 + * output buffers, etc) and capture processing + * cannot start. Failures during request processing must be + * handled by calling ICameraDeviceCallback::notify(). In case of + * this error, the framework retains responsibility for the + * stream buffers' fences and the buffer handles; the HAL must not + * close the fences or return these buffers with + * ICameraDeviceCallback::processCaptureResult(). + * INTERNAL_ERROR: + * If the camera device has encountered a serious error. After this + * error is returned, only the close() method can be successfully + * called by the framework. + * @return numRequestProcessed Number of requests successfully processed by + * camera HAL. When status is OK, this must be equal to the size of + * requests. When the call fails, this number is the number of requests + * that HAL processed successfully before HAL runs into an error. + * + */ + processCaptureRequest_3_4(vec requests, vec cachesToRemove) + generates (Status status, uint32_t numRequestProcessed); }; diff --git a/camera/device/3.4/default/CameraDeviceSession.cpp b/camera/device/3.4/default/CameraDeviceSession.cpp index b74fd5742c..c8d33ebca9 100644 --- a/camera/device/3.4/default/CameraDeviceSession.cpp +++ b/camera/device/3.4/default/CameraDeviceSession.cpp @@ -201,9 +201,9 @@ void CameraDeviceSession::postProcessConfigurationLocked_3_4( } Return CameraDeviceSession::processCaptureRequest_3_4( - const hidl_vec& requests, + const hidl_vec& requests, const hidl_vec& cachesToRemove, - ICameraDeviceSession::processCaptureRequest_cb _hidl_cb) { + ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) { updateBufferCaches(cachesToRemove); uint32_t numRequestProcessed = 0; @@ -216,14 +216,14 @@ Return CameraDeviceSession::processCaptureRequest_3_4( } if (s == Status::OK && requests.size() > 1) { - mResultBatcher.registerBatch(requests); + mResultBatcher.registerBatch(requests[0].v3_2.frameNumber, requests.size()); } _hidl_cb(s, numRequestProcessed); return Void(); } -Status CameraDeviceSession::processOneCaptureRequest_3_4(const CaptureRequest& request) { +Status CameraDeviceSession::processOneCaptureRequest_3_4(const V3_4::CaptureRequest& request) { Status status = initStatus(); if (status != Status::OK) { ALOGE("%s: camera init failed or disconnected", __FUNCTION__); @@ -231,15 +231,15 @@ Status CameraDeviceSession::processOneCaptureRequest_3_4(const CaptureRequest& r } camera3_capture_request_t halRequest; - halRequest.frame_number = request.frameNumber; + halRequest.frame_number = request.v3_2.frameNumber; bool converted = true; V3_2::CameraMetadata settingsFmq; // settings from FMQ - if (request.fmqSettingsSize > 0) { + if (request.v3_2.fmqSettingsSize > 0) { // non-blocking read; client must write metadata before calling // processOneCaptureRequest - settingsFmq.resize(request.fmqSettingsSize); - bool read = mRequestMetadataQueue->read(settingsFmq.data(), request.fmqSettingsSize); + settingsFmq.resize(request.v3_2.fmqSettingsSize); + bool read = mRequestMetadataQueue->read(settingsFmq.data(), request.v3_2.fmqSettingsSize); if (read) { converted = V3_2::implementation::convertFromHidl(settingsFmq, &halRequest.settings); } else { @@ -247,7 +247,8 @@ Status CameraDeviceSession::processOneCaptureRequest_3_4(const CaptureRequest& r converted = false; } } else { - converted = V3_2::implementation::convertFromHidl(request.settings, &halRequest.settings); + converted = V3_2::implementation::convertFromHidl(request.v3_2.settings, + &halRequest.settings); } if (!converted) { @@ -263,9 +264,9 @@ Status CameraDeviceSession::processOneCaptureRequest_3_4(const CaptureRequest& r hidl_vec allBufPtrs; hidl_vec allFences; - bool hasInputBuf = (request.inputBuffer.streamId != -1 && - request.inputBuffer.bufferId != 0); - size_t numOutputBufs = request.outputBuffers.size(); + bool hasInputBuf = (request.v3_2.inputBuffer.streamId != -1 && + request.v3_2.inputBuffer.bufferId != 0); + size_t numOutputBufs = request.v3_2.outputBuffers.size(); size_t numBufs = numOutputBufs + (hasInputBuf ? 1 : 0); if (numOutputBufs == 0) { @@ -273,7 +274,7 @@ Status CameraDeviceSession::processOneCaptureRequest_3_4(const CaptureRequest& r return Status::ILLEGAL_ARGUMENT; } - status = importRequest(request, allBufPtrs, allFences); + status = importRequest(request.v3_2, allBufPtrs, allFences); if (status != Status::OK) { return status; } @@ -285,12 +286,12 @@ Status CameraDeviceSession::processOneCaptureRequest_3_4(const CaptureRequest& r { Mutex::Autolock _l(mInflightLock); if (hasInputBuf) { - auto streamId = request.inputBuffer.streamId; - auto key = std::make_pair(request.inputBuffer.streamId, request.frameNumber); + auto streamId = request.v3_2.inputBuffer.streamId; + auto key = std::make_pair(request.v3_2.inputBuffer.streamId, request.v3_2.frameNumber); auto& bufCache = mInflightBuffers[key] = camera3_stream_buffer_t{}; convertFromHidl( - allBufPtrs[numOutputBufs], request.inputBuffer.status, - &mStreamMap[request.inputBuffer.streamId], allFences[numOutputBufs], + allBufPtrs[numOutputBufs], request.v3_2.inputBuffer.status, + &mStreamMap[request.v3_2.inputBuffer.streamId], allFences[numOutputBufs], &bufCache); bufCache.stream->physical_camera_id = mPhysicalCameraIdMap[streamId].c_str(); halRequest.input_buffer = &bufCache; @@ -300,11 +301,11 @@ Status CameraDeviceSession::processOneCaptureRequest_3_4(const CaptureRequest& r halRequest.num_output_buffers = numOutputBufs; for (size_t i = 0; i < numOutputBufs; i++) { - auto streamId = request.outputBuffers[i].streamId; - auto key = std::make_pair(streamId, request.frameNumber); + auto streamId = request.v3_2.outputBuffers[i].streamId; + auto key = std::make_pair(streamId, request.v3_2.frameNumber); auto& bufCache = mInflightBuffers[key] = camera3_stream_buffer_t{}; convertFromHidl( - allBufPtrs[i], request.outputBuffers[i].status, + allBufPtrs[i], request.v3_2.outputBuffers[i].status, &mStreamMap[streamId], allFences[i], &bufCache); bufCache.stream->physical_camera_id = mPhysicalCameraIdMap[streamId].c_str(); @@ -322,7 +323,47 @@ Status CameraDeviceSession::processOneCaptureRequest_3_4(const CaptureRequest& r } } - ATRACE_ASYNC_BEGIN("frame capture", request.frameNumber); + std::vector physicalCameraIds; + std::vector physicalCameraSettings; + std::vector physicalFmq; + size_t settingsCount = request.physicalCameraSettings.size(); + if (settingsCount > 0) { + physicalCameraIds.reserve(settingsCount); + physicalCameraSettings.reserve(settingsCount); + physicalFmq.reserve(settingsCount); + + for (size_t i = 0; i < settingsCount; i++) { + uint64_t settingsSize = request.physicalCameraSettings[i].fmqSettingsSize; + const camera_metadata_t *settings; + if (settingsSize > 0) { + physicalFmq.push_back(V3_2::CameraMetadata(settingsSize)); + bool read = mRequestMetadataQueue->read(physicalFmq[i].data(), settingsSize); + if (read) { + converted = V3_2::implementation::convertFromHidl(physicalFmq[i], &settings); + physicalCameraSettings.push_back(settings); + } else { + ALOGE("%s: physical camera settings metadata couldn't be read from fmq!", + __FUNCTION__); + converted = false; + } + } else { + converted = V3_2::implementation::convertFromHidl( + request.physicalCameraSettings[i].settings, &settings); + physicalCameraSettings.push_back(settings); + } + + if (!converted) { + ALOGE("%s: physical camera settings metadata is corrupt!", __FUNCTION__); + return Status::ILLEGAL_ARGUMENT; + } + physicalCameraIds.push_back(request.physicalCameraSettings[i].physicalCameraId.c_str()); + } + } + halRequest.num_physcam_settings = settingsCount; + halRequest.physcam_id = physicalCameraIds.data(); + halRequest.physcam_settings = physicalCameraSettings.data(); + + ATRACE_ASYNC_BEGIN("frame capture", request.v3_2.frameNumber); ATRACE_BEGIN("camera3->process_capture_request"); status_t ret = mDevice->ops->process_capture_request(mDevice, &halRequest); ATRACE_END(); @@ -335,17 +376,23 @@ Status CameraDeviceSession::processOneCaptureRequest_3_4(const CaptureRequest& r cleanupInflightFences(allFences, numBufs); if (hasInputBuf) { - auto key = std::make_pair(request.inputBuffer.streamId, request.frameNumber); + auto key = std::make_pair(request.v3_2.inputBuffer.streamId, request.v3_2.frameNumber); mInflightBuffers.erase(key); } for (size_t i = 0; i < numOutputBufs; i++) { - auto key = std::make_pair(request.outputBuffers[i].streamId, request.frameNumber); + auto key = std::make_pair(request.v3_2.outputBuffers[i].streamId, + request.v3_2.frameNumber); mInflightBuffers.erase(key); } if (aeCancelTriggerNeeded) { - mInflightAETriggerOverrides.erase(request.frameNumber); + mInflightAETriggerOverrides.erase(request.v3_2.frameNumber); + } + + if (ret == BAD_VALUE) { + return Status::ILLEGAL_ARGUMENT; + } else { + return Status::INTERNAL_ERROR; } - return Status::INTERNAL_ERROR; } mFirstRequest = false; diff --git a/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h b/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h index 83b3afe06a..fbde083fd0 100644 --- a/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h +++ b/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h @@ -85,10 +85,10 @@ protected: void postProcessConfigurationLocked_3_4(const StreamConfiguration& requestedConfiguration); Return processCaptureRequest_3_4( - const hidl_vec& requests, + const hidl_vec& requests, const hidl_vec& cachesToRemove, - ICameraDeviceSession::processCaptureRequest_cb _hidl_cb); - Status processOneCaptureRequest_3_4(const CaptureRequest& request); + ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb); + Status processOneCaptureRequest_3_4(const V3_4::CaptureRequest& request); std::map mPhysicalCameraIdMap; private: @@ -109,10 +109,16 @@ private: return mParent->configureStreams(requestedConfiguration, _hidl_cb); } + virtual Return processCaptureRequest_3_4(const hidl_vec& requests, + const hidl_vec& cachesToRemove, + ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) override { + return mParent->processCaptureRequest_3_4(requests, cachesToRemove, _hidl_cb); + } + virtual Return processCaptureRequest(const hidl_vec& requests, const hidl_vec& cachesToRemove, V3_3::ICameraDeviceSession::processCaptureRequest_cb _hidl_cb) override { - return mParent->processCaptureRequest_3_4(requests, cachesToRemove, _hidl_cb); + return mParent->processCaptureRequest(requests, cachesToRemove, _hidl_cb); } virtual Return getCaptureRequestMetadataQueue( diff --git a/camera/device/3.4/types.hal b/camera/device/3.4/types.hal index acad093193..77e855f129 100644 --- a/camera/device/3.4/types.hal +++ b/camera/device/3.4/types.hal @@ -21,6 +21,7 @@ import @3.2::StreamConfigurationMode; import @3.2::Stream; import @3.3::HalStream; import @3.2::CameraMetadata; +import @3.2::CaptureRequest; /** * Stream: @@ -133,3 +134,65 @@ struct HalStream { struct HalStreamConfiguration { vec streams; }; + +/** + * PhysicalCameraSetting: + * + * Individual camera settings for logical camera backed by multiple physical devices. + * Clients are allowed to pass separate settings for each physical device. + */ +struct PhysicalCameraSetting { + /** + * If non-zero, read settings from request queue instead + * (see ICameraDeviceSession.getCaptureRequestMetadataQueue). + * If zero, read settings from .settings field. + */ + uint64_t fmqSettingsSize; + + /** + * Contains the physical device camera id. Any settings passed by client here + * should be applied for this physical device. In case the physical id is invalid + * Hal should fail the process request and return Status::ILLEGAL_ARGUMENT. + */ + string physicalCameraId; + + /** + * If fmqSettingsSize is zero, the settings buffer contains the capture and + * processing parameters for the physical device with id 'phyCamId'. + * In case the individual settings are empty or missing, Hal should fail the + * request and return Status::ILLEGAL_ARGUMENT. + */ + CameraMetadata settings; +}; + +/** + * CaptureRequest: + * + * A single request for image capture/buffer reprocessing, sent to the Camera + * HAL device by the framework in processCaptureRequest(). + * + * The request contains the settings to be used for this capture, and the set of + * output buffers to write the resulting image data in. It may optionally + * contain an input buffer, in which case the request is for reprocessing that + * input buffer instead of capturing a new image with the camera sensor. The + * capture is identified by the frameNumber. + * + * In response, the camera HAL device must send a CaptureResult + * structure asynchronously to the framework, using the processCaptureResult() + * callback. + * + * Identical to @3.2::CaptureRequest, except that it contains @3.4::physCamSettings vector. + * + */ +struct CaptureRequest { + /** + * The definition of CaptureRequest from prior version. + */ + @3.2::CaptureRequest v3_2; + + /** + * A vector containing individual camera settings for logical camera backed by multiple physical + * devices. In case the vector is empty, Hal should use the settings field in 'v3_2'. + */ + vec physicalCameraSettings; +}; diff --git a/camera/metadata/3.3/types.hal b/camera/metadata/3.3/types.hal index 1a6e7a9cb4..6bb449e383 100644 --- a/camera/metadata/3.3/types.hal +++ b/camera/metadata/3.3/types.hal @@ -83,6 +83,13 @@ enum CameraMetadataTag : @3.2::CameraMetadataTag { */ ANDROID_REQUEST_AVAILABLE_SESSION_KEYS = android.hardware.camera.metadata@3.2::CameraMetadataTag:ANDROID_REQUEST_END, + /** android.request.availablePhysicalCameraRequestKeys [static, int32[], hidden] + * + *

A subset of the available request keys that can be overriden for + * physical devices backing a logical multi-camera.

+ */ + ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS, + ANDROID_REQUEST_END_3_3, /** android.info.version [static, byte, public] diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp index eb157663dc..4652efd16e 100644 --- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp +++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp @@ -537,6 +537,8 @@ public: Return notify(const hidl_vec& msgs) override; private: + bool processCaptureResultLocked(const CaptureResult& results); + CameraHidlTest *mParent; // Parent object }; @@ -637,6 +639,9 @@ public: std::vector &outputStreams, const AvailableStream *threshold = nullptr); static Status isConstrainedModeAvailable(camera_metadata_t *staticMeta); + static Status isLogicalMultiCamera(camera_metadata_t *staticMeta); + static Status getPhysicalCameraIds(camera_metadata_t *staticMeta, + std::vector *physicalIds/*out*/); static Status pickConstrainedModeSize(camera_metadata_t *staticMeta, AvailableStream &hfrStream); static Status isZSLModeAvailable(camera_metadata_t *staticMeta); @@ -848,121 +853,7 @@ Return CameraHidlTest::DeviceCb::processCaptureResult( bool notify = false; std::unique_lock l(mParent->mLock); for (size_t i = 0 ; i < results.size(); i++) { - uint32_t frameNumber = results[i].frameNumber; - - if ((results[i].result.size() == 0) && - (results[i].outputBuffers.size() == 0) && - (results[i].inputBuffer.buffer == nullptr) && - (results[i].fmqResultSize == 0)) { - ALOGE("%s: No result data provided by HAL for frame %d result count: %d", - __func__, frameNumber, (int) results[i].fmqResultSize); - ADD_FAILURE(); - break; - } - - ssize_t idx = mParent->mInflightMap.indexOfKey(frameNumber); - if (::android::NAME_NOT_FOUND == idx) { - ALOGE("%s: Unexpected frame number! received: %u", - __func__, frameNumber); - ADD_FAILURE(); - break; - } - - bool isPartialResult = false; - bool hasInputBufferInRequest = false; - InFlightRequest *request = mParent->mInflightMap.editValueAt(idx); - ::android::hardware::camera::device::V3_2::CameraMetadata resultMetadata; - size_t resultSize = 0; - if (results[i].fmqResultSize > 0) { - resultMetadata.resize(results[i].fmqResultSize); - if (request->resultQueue == nullptr) { - ADD_FAILURE(); - break; - } - if (!request->resultQueue->read(resultMetadata.data(), - results[i].fmqResultSize)) { - ALOGE("%s: Frame %d: Cannot read camera metadata from fmq," - "size = %" PRIu64, __func__, frameNumber, - results[i].fmqResultSize); - ADD_FAILURE(); - break; - } - resultSize = resultMetadata.size(); - } else if (results[i].result.size() > 0) { - resultMetadata.setToExternal(const_cast( - results[i].result.data()), results[i].result.size()); - resultSize = resultMetadata.size(); - } - - if (!request->usePartialResult && (resultSize > 0) && - (results[i].partialResult != 1)) { - ALOGE("%s: Result is malformed for frame %d: partial_result %u " - "must be 1 if partial result is not supported", __func__, - frameNumber, results[i].partialResult); - ADD_FAILURE(); - break; - } - - if (results[i].partialResult != 0) { - request->partialResultCount = results[i].partialResult; - } - - // Check if this result carries only partial metadata - if (request->usePartialResult && (resultSize > 0)) { - if ((results[i].partialResult > request->numPartialResults) || - (results[i].partialResult < 1)) { - ALOGE("%s: Result is malformed for frame %d: partial_result %u" - " must be in the range of [1, %d] when metadata is " - "included in the result", __func__, frameNumber, - results[i].partialResult, request->numPartialResults); - ADD_FAILURE(); - break; - } - request->collectedResult.append( - reinterpret_cast( - resultMetadata.data())); - - isPartialResult = - (results[i].partialResult < request->numPartialResults); - } - - hasInputBufferInRequest = request->hasInputBuffer; - - // Did we get the (final) result metadata for this capture? - if ((resultSize > 0) && !isPartialResult) { - if (request->haveResultMetadata) { - ALOGE("%s: Called multiple times with metadata for frame %d", - __func__, frameNumber); - ADD_FAILURE(); - break; - } - request->haveResultMetadata = true; - request->collectedResult.sort(); - } - - uint32_t numBuffersReturned = results[i].outputBuffers.size(); - if (results[i].inputBuffer.buffer != nullptr) { - if (hasInputBufferInRequest) { - numBuffersReturned += 1; - } else { - ALOGW("%s: Input buffer should be NULL if there is no input" - " buffer sent in the request", __func__); - } - } - request->numBuffersLeft -= numBuffersReturned; - if (request->numBuffersLeft < 0) { - ALOGE("%s: Too many buffers returned for frame %d", __func__, - frameNumber); - ADD_FAILURE(); - break; - } - - request->resultOutputBuffers.appendArray(results[i].outputBuffers.data(), - results[i].outputBuffers.size()); - // If shutter event is received notify the pending threads. - if (request->shutterTimestamp != 0) { - notify = true; - } + notify = processCaptureResultLocked(results[i]); } l.unlock(); @@ -973,6 +864,126 @@ Return CameraHidlTest::DeviceCb::processCaptureResult( return Void(); } +bool CameraHidlTest::DeviceCb::processCaptureResultLocked(const CaptureResult& results) { + bool notify = false; + uint32_t frameNumber = results.frameNumber; + + if ((results.result.size() == 0) && + (results.outputBuffers.size() == 0) && + (results.inputBuffer.buffer == nullptr) && + (results.fmqResultSize == 0)) { + ALOGE("%s: No result data provided by HAL for frame %d result count: %d", + __func__, frameNumber, (int) results.fmqResultSize); + ADD_FAILURE(); + return notify; + } + + ssize_t idx = mParent->mInflightMap.indexOfKey(frameNumber); + if (::android::NAME_NOT_FOUND == idx) { + ALOGE("%s: Unexpected frame number! received: %u", + __func__, frameNumber); + ADD_FAILURE(); + return notify; + } + + bool isPartialResult = false; + bool hasInputBufferInRequest = false; + InFlightRequest *request = mParent->mInflightMap.editValueAt(idx); + ::android::hardware::camera::device::V3_2::CameraMetadata resultMetadata; + size_t resultSize = 0; + if (results.fmqResultSize > 0) { + resultMetadata.resize(results.fmqResultSize); + if (request->resultQueue == nullptr) { + ADD_FAILURE(); + return notify; + } + if (!request->resultQueue->read(resultMetadata.data(), + results.fmqResultSize)) { + ALOGE("%s: Frame %d: Cannot read camera metadata from fmq," + "size = %" PRIu64, __func__, frameNumber, + results.fmqResultSize); + ADD_FAILURE(); + return notify; + } + resultSize = resultMetadata.size(); + } else if (results.result.size() > 0) { + resultMetadata.setToExternal(const_cast( + results.result.data()), results.result.size()); + resultSize = resultMetadata.size(); + } + + if (!request->usePartialResult && (resultSize > 0) && + (results.partialResult != 1)) { + ALOGE("%s: Result is malformed for frame %d: partial_result %u " + "must be 1 if partial result is not supported", __func__, + frameNumber, results.partialResult); + ADD_FAILURE(); + return notify; + } + + if (results.partialResult != 0) { + request->partialResultCount = results.partialResult; + } + + // Check if this result carries only partial metadata + if (request->usePartialResult && (resultSize > 0)) { + if ((results.partialResult > request->numPartialResults) || + (results.partialResult < 1)) { + ALOGE("%s: Result is malformed for frame %d: partial_result %u" + " must be in the range of [1, %d] when metadata is " + "included in the result", __func__, frameNumber, + results.partialResult, request->numPartialResults); + ADD_FAILURE(); + return notify; + } + request->collectedResult.append( + reinterpret_cast( + resultMetadata.data())); + + isPartialResult = + (results.partialResult < request->numPartialResults); + } + + hasInputBufferInRequest = request->hasInputBuffer; + + // Did we get the (final) result metadata for this capture? + if ((resultSize > 0) && !isPartialResult) { + if (request->haveResultMetadata) { + ALOGE("%s: Called multiple times with metadata for frame %d", + __func__, frameNumber); + ADD_FAILURE(); + return notify; + } + request->haveResultMetadata = true; + request->collectedResult.sort(); + } + + uint32_t numBuffersReturned = results.outputBuffers.size(); + if (results.inputBuffer.buffer != nullptr) { + if (hasInputBufferInRequest) { + numBuffersReturned += 1; + } else { + ALOGW("%s: Input buffer should be NULL if there is no input" + " buffer sent in the request", __func__); + } + } + request->numBuffersLeft -= numBuffersReturned; + if (request->numBuffersLeft < 0) { + ALOGE("%s: Too many buffers returned for frame %d", __func__, + frameNumber); + ADD_FAILURE(); + return notify; + } + + request->resultOutputBuffers.appendArray(results.outputBuffers.data(), + results.outputBuffers.size()); + // If shutter event is received notify the pending threads. + if (request->shutterTimestamp != 0) { + notify = true; + } + return notify; +} + Return CameraHidlTest::DeviceCb::notify( const hidl_vec& messages) { std::lock_guard l(mParent->mLock); @@ -3289,6 +3300,171 @@ TEST_F(CameraHidlTest, processCaptureRequestPreview) { } } +// Generate and verify a multi-camera capture request +TEST_F(CameraHidlTest, processMultiCaptureRequestPreview) { + hidl_vec cameraDeviceNames = getCameraDeviceNames(mProvider); + AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, + static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; + uint64_t bufferId = 1; + uint32_t frameNumber = 1; + ::android::hardware::hidl_vec settings; + ::android::hardware::hidl_vec emptySettings; + hidl_string invalidPhysicalId = "-1"; + + for (const auto& name : cameraDeviceNames) { + int deviceVersion = getCameraDeviceVersion(name, mProviderType); + if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_4) { + continue; + } + camera_metadata_t* staticMeta; + Return ret; + sp session; + openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/); + + Status rc = isLogicalMultiCamera(staticMeta); + if (Status::METHOD_NOT_SUPPORTED == rc) { + ret = session->close(); + ASSERT_TRUE(ret.isOk()); + continue; + } + std::vector physicalIds; + rc = getPhysicalCameraIds(staticMeta, &physicalIds); + ASSERT_TRUE(Status::OK == rc); + ASSERT_TRUE(physicalIds.size() > 1); + + free_camera_metadata(staticMeta); + ret = session->close(); + ASSERT_TRUE(ret.isOk()); + + V3_2::Stream previewStream; + HalStreamConfiguration halStreamConfig; + bool supportsPartialResults = false; + uint32_t partialResultCount = 0; + configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/, + &previewStream /*out*/, &halStreamConfig /*out*/, + &supportsPartialResults /*out*/, + &partialResultCount /*out*/); + sp session3_3; + sp session3_4; + castSession(session, deviceVersion, &session3_3, &session3_4); + ASSERT_NE(session3_4, nullptr); + + std::shared_ptr resultQueue; + auto resultQueueRet = + session->getCaptureResultMetadataQueue( + [&resultQueue](const auto& descriptor) { + resultQueue = std::make_shared( + descriptor); + if (!resultQueue->isValid() || + resultQueue->availableToWrite() <= 0) { + ALOGE("%s: HAL returns empty result metadata fmq," + " not use it", __func__); + resultQueue = nullptr; + // Don't use the queue onwards. + } + }); + ASSERT_TRUE(resultQueueRet.isOk()); + + InFlightRequest inflightReq = {1, false, supportsPartialResults, + partialResultCount, resultQueue}; + + RequestTemplate reqTemplate = RequestTemplate::PREVIEW; + ret = session->constructDefaultRequestSettings(reqTemplate, + [&](auto status, const auto& req) { + ASSERT_EQ(Status::OK, status); + settings = req; + }); + ASSERT_TRUE(ret.isOk()); + + sp gb = new GraphicBuffer( + previewStream.width, previewStream.height, + static_cast(halStreamConfig.streams[0].overrideFormat), 1, + android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage, + halStreamConfig.streams[0].consumerUsage)); + ASSERT_NE(nullptr, gb.get()); + ::android::hardware::hidl_vec outputBuffers; + outputBuffers.resize(1); + outputBuffers[0] = {halStreamConfig.streams[0].id, + bufferId, + hidl_handle(gb->getNativeBuffer()->handle), + BufferStatus::OK, + nullptr, + nullptr}; + + StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr, + nullptr}; + hidl_vec camSettings; + camSettings.resize(2); + camSettings[0] = {0, hidl_string(physicalIds[0]), settings}; + camSettings[1] = {0, hidl_string(physicalIds[1]), settings}; + V3_4::CaptureRequest request = {{frameNumber, 0 /* fmqSettingsSize */, settings, + emptyInputBuffer, outputBuffers}, camSettings}; + + { + std::unique_lock l(mLock); + mInflightMap.clear(); + mInflightMap.add(frameNumber, &inflightReq); + } + + Status stat = Status::INTERNAL_ERROR; + uint32_t numRequestProcessed = 0; + hidl_vec cachesToRemove; + Return returnStatus = session3_4->processCaptureRequest_3_4( + {request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) { + stat = s; + numRequestProcessed = n; + }); + ASSERT_TRUE(returnStatus.isOk()); + ASSERT_EQ(Status::OK, stat); + ASSERT_EQ(numRequestProcessed, 1u); + + { + std::unique_lock l(mLock); + while (!inflightReq.errorCodeValid && + ((0 < inflightReq.numBuffersLeft) || + (!inflightReq.haveResultMetadata))) { + auto timeout = std::chrono::system_clock::now() + + std::chrono::seconds(kStreamBufferTimeoutSec); + ASSERT_NE(std::cv_status::timeout, + mResultCondition.wait_until(l, timeout)); + } + + ASSERT_FALSE(inflightReq.errorCodeValid); + ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u); + ASSERT_EQ(halStreamConfig.streams[0].id, + inflightReq.resultOutputBuffers[0].streamId); + } + + // Empty physical camera settings should fail process requests + camSettings[1] = {0, hidl_string(physicalIds[1]), emptySettings}; + frameNumber++; + request = {{frameNumber, 0 /* fmqSettingsSize */, settings, + emptyInputBuffer, outputBuffers}, camSettings}; + returnStatus = session3_4->processCaptureRequest_3_4( + {request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) { + stat = s; + numRequestProcessed = n; + }); + ASSERT_TRUE(returnStatus.isOk()); + ASSERT_EQ(Status::ILLEGAL_ARGUMENT, stat); + + // Invalid physical camera id should fail process requests + camSettings[1] = {0, invalidPhysicalId, settings}; + request = {{frameNumber, 0 /* fmqSettingsSize */, settings, + emptyInputBuffer, outputBuffers}, camSettings}; + returnStatus = session3_4->processCaptureRequest_3_4( + {request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) { + stat = s; + numRequestProcessed = n; + }); + ASSERT_TRUE(returnStatus.isOk()); + ASSERT_EQ(Status::ILLEGAL_ARGUMENT, stat); + + ret = session->close(); + ASSERT_TRUE(ret.isOk()); + } +} + // Test whether an incorrect capture request with missing settings will // be reported correctly. TEST_F(CameraHidlTest, processCaptureRequestInvalidSinglePreview) { @@ -3636,6 +3812,59 @@ Status CameraHidlTest::getAvailableOutputStreams(camera_metadata_t *staticMeta, return Status::OK; } +// Check if the camera device has logical multi-camera capability. +Status CameraHidlTest::isLogicalMultiCamera(camera_metadata_t *staticMeta) { + Status ret = Status::METHOD_NOT_SUPPORTED; + if (nullptr == staticMeta) { + return Status::ILLEGAL_ARGUMENT; + } + + camera_metadata_ro_entry entry; + int rc = find_camera_metadata_ro_entry(staticMeta, + ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry); + if (0 != rc) { + return Status::ILLEGAL_ARGUMENT; + } + + for (size_t i = 0; i < entry.count; i++) { + if (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA == entry.data.u8[i]) { + ret = Status::OK; + break; + } + } + + return ret; +} + +// Generate a list of physical camera ids backing a logical multi-camera. +Status CameraHidlTest::getPhysicalCameraIds(camera_metadata_t *staticMeta, + std::vector *physicalIds) { + if ((nullptr == staticMeta) || (nullptr == physicalIds)) { + return Status::ILLEGAL_ARGUMENT; + } + + camera_metadata_ro_entry entry; + int rc = find_camera_metadata_ro_entry(staticMeta, ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS, + &entry); + if (0 != rc) { + return Status::ILLEGAL_ARGUMENT; + } + + const uint8_t* ids = entry.data.u8; + size_t start = 0; + for (size_t i = 0; i < entry.count; i++) { + if (ids[i] == '\0') { + if (start != i) { + std::string currentId(reinterpret_cast (ids + start)); + physicalIds->push_back(currentId); + } + start = i + 1; + } + } + + return Status::OK; +} + // Check if constrained mode is supported by using the static // camera characteristics. Status CameraHidlTest::isConstrainedModeAvailable(camera_metadata_t *staticMeta) {