Merge "camera: Add support for individual physical camera requests"

This commit is contained in:
Shuzhen Wang 2018-01-23 00:36:17 +00:00 committed by Android (Google) Code Review
commit 30170d5590
8 changed files with 536 additions and 151 deletions

View file

@ -333,11 +333,10 @@ void CameraDeviceSession::ResultBatcher::setResultMetadataQueue(
mResultMetadataQueue = q;
}
void CameraDeviceSession::ResultBatcher::registerBatch(
const hidl_vec<CaptureRequest>& requests) {
void CameraDeviceSession::ResultBatcher::registerBatch(uint32_t frameNumber, uint32_t batchSize) {
auto batch = std::make_shared<InflightBatch>();
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<void> 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");

View file

@ -184,7 +184,7 @@ protected:
void setBatchedStreams(const std::vector<int>& streamsToBatch);
void setResultMetadataQueue(std::shared_ptr<ResultMetadataQueue> q);
void registerBatch(const hidl_vec<CaptureRequest>& requests);
void registerBatch(uint32_t frameNumber, uint32_t batchSize);
void notify(NotifyMsg& msg);
void processCaptureResult(CaptureResult& result);

View file

@ -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<CaptureRequest> requests, vec<BufferCache> cachesToRemove)
generates (Status status, uint32_t numRequestProcessed);
};

View file

@ -201,9 +201,9 @@ void CameraDeviceSession::postProcessConfigurationLocked_3_4(
}
Return<void> CameraDeviceSession::processCaptureRequest_3_4(
const hidl_vec<CaptureRequest>& requests,
const hidl_vec<V3_4::CaptureRequest>& requests,
const hidl_vec<V3_2::BufferCache>& cachesToRemove,
ICameraDeviceSession::processCaptureRequest_cb _hidl_cb) {
ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) {
updateBufferCaches(cachesToRemove);
uint32_t numRequestProcessed = 0;
@ -216,14 +216,14 @@ Return<void> 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<buffer_handle_t*> allBufPtrs;
hidl_vec<int> 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<const char *> physicalCameraIds;
std::vector<const camera_metadata_t *> physicalCameraSettings;
std::vector<V3_2::CameraMetadata> 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;

View file

@ -85,10 +85,10 @@ protected:
void postProcessConfigurationLocked_3_4(const StreamConfiguration& requestedConfiguration);
Return<void> processCaptureRequest_3_4(
const hidl_vec<CaptureRequest>& requests,
const hidl_vec<V3_4::CaptureRequest>& requests,
const hidl_vec<V3_2::BufferCache>& 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<int, std::string> mPhysicalCameraIdMap;
private:
@ -109,10 +109,16 @@ private:
return mParent->configureStreams(requestedConfiguration, _hidl_cb);
}
virtual Return<void> processCaptureRequest_3_4(const hidl_vec<V3_4::CaptureRequest>& requests,
const hidl_vec<V3_2::BufferCache>& cachesToRemove,
ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) override {
return mParent->processCaptureRequest_3_4(requests, cachesToRemove, _hidl_cb);
}
virtual Return<void> processCaptureRequest(const hidl_vec<V3_2::CaptureRequest>& requests,
const hidl_vec<V3_2::BufferCache>& 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<void> getCaptureRequestMetadataQueue(

View file

@ -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<HalStream> 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<PhysicalCameraSetting> physicalCameraSettings;
};

View file

@ -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]
*
* <p>A subset of the available request keys that can be overriden for
* physical devices backing a logical multi-camera.</p>
*/
ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS,
ANDROID_REQUEST_END_3_3,
/** android.statistics.oisDataMode [dynamic, enum, public]

View file

@ -537,6 +537,8 @@ public:
Return<void> notify(const hidl_vec<NotifyMsg>& msgs) override;
private:
bool processCaptureResultLocked(const CaptureResult& results);
CameraHidlTest *mParent; // Parent object
};
@ -637,6 +639,9 @@ public:
std::vector<AvailableStream> &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<std::string> *physicalIds/*out*/);
static Status pickConstrainedModeSize(camera_metadata_t *staticMeta,
AvailableStream &hfrStream);
static Status isZSLModeAvailable(camera_metadata_t *staticMeta);
@ -848,121 +853,7 @@ Return<void> CameraHidlTest::DeviceCb::processCaptureResult(
bool notify = false;
std::unique_lock<std::mutex> 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<uint8_t *>(
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<const camera_metadata_t*>(
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<void> 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<uint8_t *>(
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<const camera_metadata_t*>(
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<void> CameraHidlTest::DeviceCb::notify(
const hidl_vec<NotifyMsg>& messages) {
std::lock_guard<std::mutex> 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<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
uint64_t bufferId = 1;
uint32_t frameNumber = 1;
::android::hardware::hidl_vec<uint8_t> settings;
::android::hardware::hidl_vec<uint8_t> 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<void> ret;
sp<ICameraDeviceSession> 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<std::string> 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<device::V3_3::ICameraDeviceSession> session3_3;
sp<device::V3_4::ICameraDeviceSession> session3_4;
castSession(session, deviceVersion, &session3_3, &session3_4);
ASSERT_NE(session3_4, nullptr);
std::shared_ptr<ResultMetadataQueue> resultQueue;
auto resultQueueRet =
session->getCaptureResultMetadataQueue(
[&resultQueue](const auto& descriptor) {
resultQueue = std::make_shared<ResultMetadataQueue>(
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<GraphicBuffer> gb = new GraphicBuffer(
previewStream.width, previewStream.height,
static_cast<int32_t>(halStreamConfig.streams[0].overrideFormat), 1,
android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage,
halStreamConfig.streams[0].consumerUsage));
ASSERT_NE(nullptr, gb.get());
::android::hardware::hidl_vec<StreamBuffer> 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<V3_4::PhysicalCameraSetting> 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<std::mutex> l(mLock);
mInflightMap.clear();
mInflightMap.add(frameNumber, &inflightReq);
}
Status stat = Status::INTERNAL_ERROR;
uint32_t numRequestProcessed = 0;
hidl_vec<BufferCache> cachesToRemove;
Return<void> 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<std::mutex> 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<std::string> *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<const char *> (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) {