camera: Add physical camera metadata in CaptureResult
- When physical stream of a logical multi-camera is requested, HAL needs
to generate metadata for the physical cameras.
- In case no physical stream is requested for the logical multi-camera, no
capture result metadata is required for physical camera.
- Batch physical and logical metadata within one capture_result call.
Test: testLogicalCamera CTS test
Bug: 64691172
Change-Id: Id040620f3f0c350711d49341ab31ab88ecd94888
diff --git a/camera/device/3.4/default/CameraDeviceSession.cpp b/camera/device/3.4/default/CameraDeviceSession.cpp
index d054788..f6c6b2b 100644
--- a/camera/device/3.4/default/CameraDeviceSession.cpp
+++ b/camera/device/3.4/default/CameraDeviceSession.cpp
@@ -34,7 +34,23 @@
camera3_device_t* device,
const camera_metadata_t* deviceInfo,
const sp<V3_2::ICameraDeviceCallback>& callback) :
- V3_3::implementation::CameraDeviceSession(device, deviceInfo, callback) {
+ V3_3::implementation::CameraDeviceSession(device, deviceInfo, callback),
+ mResultBatcher_3_4(callback) {
+
+ mHasCallback_3_4 = false;
+
+ auto castResult = ICameraDeviceCallback::castFrom(callback);
+ if (castResult.isOk()) {
+ sp<ICameraDeviceCallback> callback3_4 = castResult;
+ if (callback3_4 != nullptr) {
+ process_capture_result = sProcessCaptureResult_3_4;
+ notify = sNotify_3_4;
+ mHasCallback_3_4 = true;
+ if (!mInitFail) {
+ mResultBatcher_3_4.setResultMetadataQueue(mResultMetadataQueue);
+ }
+ }
+ }
}
CameraDeviceSession::~CameraDeviceSession() {
@@ -54,6 +70,18 @@
Status status = initStatus();
HalStreamConfiguration outStreams;
+ // If callback is 3.2, make sure no physical stream is configured
+ if (!mHasCallback_3_4) {
+ for (size_t i = 0; i < requestedConfiguration.streams.size(); i++) {
+ if (requestedConfiguration.streams[i].physicalCameraId.size() > 0) {
+ ALOGE("%s: trying to configureStreams with physical camera id with V3.2 callback",
+ __FUNCTION__);
+ _hidl_cb(Status::INTERNAL_ERROR, outStreams);
+ return Void();
+ }
+ }
+ }
+
// hold the inflight lock for entire configureStreams scope since there must not be any
// inflight request/results during stream configuration.
Mutex::Autolock _l(mInflightLock);
@@ -205,7 +233,7 @@
mVideoStreamIds.push_back(stream.v3_2.id);
}
}
- mResultBatcher.setBatchedStreams(mVideoStreamIds);
+ mResultBatcher_3_4.setBatchedStreams(mVideoStreamIds);
}
Return<void> CameraDeviceSession::processCaptureRequest_3_4(
@@ -224,7 +252,7 @@
}
if (s == Status::OK && requests.size() > 1) {
- mResultBatcher.registerBatch(requests[0].v3_2.frameNumber, requests.size());
+ mResultBatcher_3_4.registerBatch(requests[0].v3_2.frameNumber, requests.size());
}
_hidl_cb(s, numRequestProcessed);
@@ -237,6 +265,14 @@
ALOGE("%s: camera init failed or disconnected", __FUNCTION__);
return status;
}
+ // If callback is 3.2, make sure there are no physical settings.
+ if (!mHasCallback_3_4) {
+ if (request.physicalCameraSettings.size() > 0) {
+ ALOGE("%s: trying to call processCaptureRequest_3_4 with physical camera id "
+ "and V3.2 callback", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+ }
camera3_capture_request_t halRequest;
halRequest.frame_number = request.v3_2.frameNumber;
@@ -407,6 +443,228 @@
return Status::OK;
}
+/**
+ * Static callback forwarding methods from HAL to instance
+ */
+void CameraDeviceSession::sProcessCaptureResult_3_4(
+ const camera3_callback_ops *cb,
+ const camera3_capture_result *hal_result) {
+ CameraDeviceSession *d =
+ const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
+
+ CaptureResult result;
+ d->constructCaptureResult(result.v3_2, hal_result);
+ result.physicalCameraMetadata.resize(hal_result->num_physcam_metadata);
+ for (uint32_t i = 0; i < hal_result->num_physcam_metadata; i++) {
+ std::string physicalId = hal_result->physcam_ids[i];
+ V3_2::CameraMetadata physicalMetadata;
+ V3_2::implementation::convertToHidl(hal_result->physcam_metadata[i], &physicalMetadata);
+ PhysicalCameraMetadata physicalCameraMetadata = {
+ .fmqMetadataSize = 0,
+ .physicalCameraId = physicalId,
+ .metadata = physicalMetadata };
+ result.physicalCameraMetadata[i] = physicalCameraMetadata;
+ }
+ d->mResultBatcher_3_4.processCaptureResult_3_4(result);
+}
+
+void CameraDeviceSession::sNotify_3_4(
+ const camera3_callback_ops *cb,
+ const camera3_notify_msg *msg) {
+ CameraDeviceSession *d =
+ const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
+ V3_2::NotifyMsg hidlMsg;
+ V3_2::implementation::convertToHidl(msg, &hidlMsg);
+
+ if (hidlMsg.type == (V3_2::MsgType) CAMERA3_MSG_ERROR &&
+ hidlMsg.msg.error.errorStreamId != -1) {
+ if (d->mStreamMap.count(hidlMsg.msg.error.errorStreamId) != 1) {
+ ALOGE("%s: unknown stream ID %d reports an error!",
+ __FUNCTION__, hidlMsg.msg.error.errorStreamId);
+ return;
+ }
+ }
+
+ if (static_cast<camera3_msg_type_t>(hidlMsg.type) == CAMERA3_MSG_ERROR) {
+ switch (hidlMsg.msg.error.errorCode) {
+ case V3_2::ErrorCode::ERROR_DEVICE:
+ case V3_2::ErrorCode::ERROR_REQUEST:
+ case V3_2::ErrorCode::ERROR_RESULT: {
+ Mutex::Autolock _l(d->mInflightLock);
+ auto entry = d->mInflightAETriggerOverrides.find(
+ hidlMsg.msg.error.frameNumber);
+ if (d->mInflightAETriggerOverrides.end() != entry) {
+ d->mInflightAETriggerOverrides.erase(
+ hidlMsg.msg.error.frameNumber);
+ }
+
+ auto boostEntry = d->mInflightRawBoostPresent.find(
+ hidlMsg.msg.error.frameNumber);
+ if (d->mInflightRawBoostPresent.end() != boostEntry) {
+ d->mInflightRawBoostPresent.erase(
+ hidlMsg.msg.error.frameNumber);
+ }
+
+ }
+ break;
+ case V3_2::ErrorCode::ERROR_BUFFER:
+ default:
+ break;
+ }
+
+ }
+
+ d->mResultBatcher_3_4.notify(hidlMsg);
+}
+
+CameraDeviceSession::ResultBatcher_3_4::ResultBatcher_3_4(
+ const sp<V3_2::ICameraDeviceCallback>& callback) :
+ V3_3::implementation::CameraDeviceSession::ResultBatcher(callback) {
+ auto castResult = ICameraDeviceCallback::castFrom(callback);
+ if (castResult.isOk()) {
+ mCallback_3_4 = castResult;
+ }
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::processCaptureResult_3_4(CaptureResult& result) {
+ auto pair = getBatch(result.v3_2.frameNumber);
+ int batchIdx = pair.first;
+ if (batchIdx == NOT_BATCHED) {
+ processOneCaptureResult_3_4(result);
+ return;
+ }
+ std::shared_ptr<InflightBatch> batch = pair.second;
+ {
+ Mutex::Autolock _l(batch->mLock);
+ // Check if the batch is removed (mostly by notify error) before lock was acquired
+ if (batch->mRemoved) {
+ // Fall back to non-batch path
+ processOneCaptureResult_3_4(result);
+ return;
+ }
+
+ // queue metadata
+ if (result.v3_2.result.size() != 0) {
+ // Save a copy of metadata
+ batch->mResultMds[result.v3_2.partialResult].mMds.push_back(
+ std::make_pair(result.v3_2.frameNumber, result.v3_2.result));
+ }
+
+ // queue buffer
+ std::vector<int> filledStreams;
+ std::vector<V3_2::StreamBuffer> nonBatchedBuffers;
+ for (auto& buffer : result.v3_2.outputBuffers) {
+ auto it = batch->mBatchBufs.find(buffer.streamId);
+ if (it != batch->mBatchBufs.end()) {
+ InflightBatch::BufferBatch& bb = it->second;
+ pushStreamBuffer(std::move(buffer), bb.mBuffers);
+ filledStreams.push_back(buffer.streamId);
+ } else {
+ pushStreamBuffer(std::move(buffer), nonBatchedBuffers);
+ }
+ }
+
+ // send non-batched buffers up
+ if (nonBatchedBuffers.size() > 0 || result.v3_2.inputBuffer.streamId != -1) {
+ CaptureResult nonBatchedResult;
+ nonBatchedResult.v3_2.frameNumber = result.v3_2.frameNumber;
+ nonBatchedResult.v3_2.fmqResultSize = 0;
+ nonBatchedResult.v3_2.outputBuffers.resize(nonBatchedBuffers.size());
+ for (size_t i = 0; i < nonBatchedBuffers.size(); i++) {
+ moveStreamBuffer(
+ std::move(nonBatchedBuffers[i]), nonBatchedResult.v3_2.outputBuffers[i]);
+ }
+ moveStreamBuffer(std::move(result.v3_2.inputBuffer), nonBatchedResult.v3_2.inputBuffer);
+ nonBatchedResult.v3_2.partialResult = 0; // 0 for buffer only results
+ processOneCaptureResult_3_4(nonBatchedResult);
+ }
+
+ if (result.v3_2.frameNumber == batch->mLastFrame) {
+ // Send data up
+ if (result.v3_2.partialResult > 0) {
+ sendBatchMetadataLocked(batch, result.v3_2.partialResult);
+ }
+ // send buffer up
+ if (filledStreams.size() > 0) {
+ sendBatchBuffersLocked(batch, filledStreams);
+ }
+ }
+ } // end of batch lock scope
+
+ // see if the batch is complete
+ if (result.v3_2.frameNumber == batch->mLastFrame) {
+ checkAndRemoveFirstBatch();
+ }
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::processOneCaptureResult_3_4(CaptureResult& result) {
+ hidl_vec<CaptureResult> results;
+ results.resize(1);
+ results[0] = std::move(result);
+ invokeProcessCaptureResultCallback_3_4(results, /* tryWriteFmq */true);
+ freeReleaseFences_3_4(results);
+ return;
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::invokeProcessCaptureResultCallback_3_4(
+ hidl_vec<CaptureResult> &results, bool tryWriteFmq) {
+ if (mProcessCaptureResultLock.tryLock() != OK) {
+ ALOGV("%s: previous call is not finished! waiting 1s...", __FUNCTION__);
+ if (mProcessCaptureResultLock.timedLock(1000000000 /* 1s */) != OK) {
+ ALOGE("%s: cannot acquire lock in 1s, cannot proceed",
+ __FUNCTION__);
+ return;
+ }
+ }
+ if (tryWriteFmq && mResultMetadataQueue->availableToWrite() > 0) {
+ for (CaptureResult &result : results) {
+ if (result.v3_2.result.size() > 0) {
+ if (mResultMetadataQueue->write(result.v3_2.result.data(),
+ result.v3_2.result.size())) {
+ result.v3_2.fmqResultSize = result.v3_2.result.size();
+ result.v3_2.result.resize(0);
+ } else {
+ ALOGW("%s: couldn't utilize fmq, fall back to hwbinder", __FUNCTION__);
+ result.v3_2.fmqResultSize = 0;
+ }
+ }
+
+ for (auto& onePhysMetadata : result.physicalCameraMetadata) {
+ if (mResultMetadataQueue->write(onePhysMetadata.metadata.data(),
+ onePhysMetadata.metadata.size())) {
+ onePhysMetadata.fmqMetadataSize = onePhysMetadata.metadata.size();
+ onePhysMetadata.metadata.resize(0);
+ } else {
+ ALOGW("%s: couldn't utilize fmq, fall back to hwbinder", __FUNCTION__);
+ onePhysMetadata.fmqMetadataSize = 0;
+ }
+ }
+ }
+ }
+ mCallback_3_4->processCaptureResult_3_4(results);
+ mProcessCaptureResultLock.unlock();
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::freeReleaseFences_3_4(hidl_vec<CaptureResult>& results) {
+ for (auto& result : results) {
+ if (result.v3_2.inputBuffer.releaseFence.getNativeHandle() != nullptr) {
+ native_handle_t* handle = const_cast<native_handle_t*>(
+ result.v3_2.inputBuffer.releaseFence.getNativeHandle());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ for (auto& buf : result.v3_2.outputBuffers) {
+ if (buf.releaseFence.getNativeHandle() != nullptr) {
+ native_handle_t* handle = const_cast<native_handle_t*>(
+ buf.releaseFence.getNativeHandle());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ }
+ }
+ return;
+}
+
} // namespace implementation
} // namespace V3_4
} // namespace device