| /* |
| * Copyright (C) 2020 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 "ExtCamDevSsn@3.6" |
| #include <android/log.h> |
| |
| #include <utils/Trace.h> |
| #include "ExternalCameraDeviceSession.h" |
| |
| namespace android { |
| namespace hardware { |
| namespace camera { |
| namespace device { |
| namespace V3_6 { |
| namespace implementation { |
| |
| ExternalCameraDeviceSession::ExternalCameraDeviceSession( |
| const sp<V3_2::ICameraDeviceCallback>& callback, |
| const ExternalCameraConfig& cfg, |
| const std::vector<SupportedV4L2Format>& sortedFormats, |
| const CroppingType& croppingType, |
| const common::V1_0::helper::CameraMetadata& chars, |
| const std::string& cameraId, |
| unique_fd v4l2Fd) : |
| V3_5::implementation::ExternalCameraDeviceSession( |
| callback, cfg, sortedFormats, croppingType, chars, cameraId, std::move(v4l2Fd)) { |
| } |
| |
| ExternalCameraDeviceSession::~ExternalCameraDeviceSession() {} |
| |
| |
| Return<void> ExternalCameraDeviceSession::configureStreams_3_6( |
| const StreamConfiguration& requestedConfiguration, |
| ICameraDeviceSession::configureStreams_3_6_cb _hidl_cb) { |
| V3_2::StreamConfiguration config_v32; |
| V3_3::HalStreamConfiguration outStreams_v33; |
| V3_6::HalStreamConfiguration outStreams; |
| const V3_4::StreamConfiguration& requestedConfiguration_3_4 = requestedConfiguration.v3_4; |
| Mutex::Autolock _il(mInterfaceLock); |
| |
| config_v32.operationMode = requestedConfiguration_3_4.operationMode; |
| config_v32.streams.resize(requestedConfiguration_3_4.streams.size()); |
| uint32_t blobBufferSize = 0; |
| int numStallStream = 0; |
| for (size_t i = 0; i < config_v32.streams.size(); i++) { |
| config_v32.streams[i] = requestedConfiguration_3_4.streams[i].v3_2; |
| if (config_v32.streams[i].format == PixelFormat::BLOB) { |
| blobBufferSize = requestedConfiguration_3_4.streams[i].bufferSize; |
| numStallStream++; |
| } |
| } |
| |
| // Fail early if there are multiple BLOB streams |
| if (numStallStream > kMaxStallStream) { |
| ALOGE("%s: too many stall streams (expect <= %d, got %d)", __FUNCTION__, |
| kMaxStallStream, numStallStream); |
| _hidl_cb(Status::ILLEGAL_ARGUMENT, outStreams); |
| return Void(); |
| } |
| |
| Status status = configureStreams(config_v32, &outStreams_v33, blobBufferSize); |
| |
| fillOutputStream3_6(outStreams_v33, &outStreams); |
| |
| _hidl_cb(status, outStreams); |
| return Void(); |
| } |
| |
| Return<void> ExternalCameraDeviceSession::switchToOffline( |
| const hidl_vec<int32_t>& streamsToKeep, |
| ICameraDeviceSession::switchToOffline_cb _hidl_cb) { |
| std::vector<NotifyMsg> msgs; |
| std::vector<CaptureResult> results; |
| CameraOfflineSessionInfo info; |
| sp<ICameraOfflineSession> session; |
| |
| Status st = switchToOffline(streamsToKeep, &msgs, &results, &info, &session); |
| |
| mCallback->notify(msgs); |
| hidl_vec<CaptureResult> hidlResults(std::move(results)); |
| invokeProcessCaptureResultCallback(hidlResults, /* tryWriteFmq */true); |
| V3_4::implementation::freeReleaseFences(hidlResults); |
| |
| _hidl_cb(st, info, session); |
| return Void(); |
| } |
| |
| void ExternalCameraDeviceSession::fillOutputStream3_6( |
| const V3_3::HalStreamConfiguration& outStreams_v33, |
| /*out*/V3_6::HalStreamConfiguration* outStreams_v36) { |
| if (outStreams_v36 == nullptr) { |
| ALOGE("%s: outStreams_v36 must not be null!", __FUNCTION__); |
| return; |
| } |
| Mutex::Autolock _l(mLock); |
| outStreams_v36->streams.resize(outStreams_v33.streams.size()); |
| for (size_t i = 0; i < outStreams_v36->streams.size(); i++) { |
| outStreams_v36->streams[i].v3_4.v3_3 = outStreams_v33.streams[i]; |
| outStreams_v36->streams[i].supportOffline = |
| supportOfflineLocked(outStreams_v33.streams[i].v3_2.id); |
| } |
| } |
| |
| bool ExternalCameraDeviceSession::supportOfflineLocked(int32_t streamId) { |
| const Stream& stream = mStreamMap[streamId]; |
| if (stream.format == PixelFormat::BLOB && |
| stream.dataSpace == static_cast<int32_t>(Dataspace::V0_JFIF)) { |
| return true; |
| } |
| // TODO: support YUV output stream? |
| return false; |
| } |
| |
| bool ExternalCameraDeviceSession::canDropRequest(const hidl_vec<int32_t>& offlineStreams, |
| std::shared_ptr<V3_4::implementation::HalRequest> halReq) { |
| for (const auto& buffer : halReq->buffers) { |
| for (auto offlineStreamId : offlineStreams) { |
| if (buffer.streamId == offlineStreamId) { |
| return false; |
| } |
| } |
| } |
| // Only drop a request completely if it has no offline output |
| return true; |
| } |
| |
| void ExternalCameraDeviceSession::fillOfflineSessionInfo(const hidl_vec<int32_t>& offlineStreams, |
| std::deque<std::shared_ptr<HalRequest>>& offlineReqs, |
| const std::map<int, CirculatingBuffers>& circulatingBuffers, |
| /*out*/CameraOfflineSessionInfo* info) { |
| if (info == nullptr) { |
| ALOGE("%s: output info must not be null!", __FUNCTION__); |
| return; |
| } |
| |
| info->offlineStreams.resize(offlineStreams.size()); |
| info->offlineRequests.resize(offlineReqs.size()); |
| |
| // Fill in offline reqs and count outstanding buffers |
| for (size_t i = 0; i < offlineReqs.size(); i++) { |
| info->offlineRequests[i].frameNumber = offlineReqs[i]->frameNumber; |
| info->offlineRequests[i].pendingStreams.resize(offlineReqs[i]->buffers.size()); |
| for (size_t bIdx = 0; bIdx < offlineReqs[i]->buffers.size(); bIdx++) { |
| int32_t streamId = offlineReqs[i]->buffers[bIdx].streamId; |
| info->offlineRequests[i].pendingStreams[bIdx] = streamId; |
| } |
| } |
| |
| for (size_t i = 0; i < offlineStreams.size(); i++) { |
| int32_t streamId = offlineStreams[i]; |
| info->offlineStreams[i].id = streamId; |
| // outstanding buffers are 0 since we are doing hal buffer management and |
| // offline session will ask for those buffers later |
| info->offlineStreams[i].numOutstandingBuffers = 0; |
| const CirculatingBuffers& bufIdMap = circulatingBuffers.at(streamId); |
| info->offlineStreams[i].circulatingBufferIds.resize(bufIdMap.size()); |
| size_t bIdx = 0; |
| for (const auto& pair : bufIdMap) { |
| // Fill in bufferId |
| info->offlineStreams[i].circulatingBufferIds[bIdx++] = pair.first; |
| } |
| |
| } |
| } |
| |
| Status ExternalCameraDeviceSession::switchToOffline(const hidl_vec<int32_t>& offlineStreams, |
| /*out*/std::vector<NotifyMsg>* msgs, |
| /*out*/std::vector<CaptureResult>* results, |
| /*out*/CameraOfflineSessionInfo* info, |
| /*out*/sp<ICameraOfflineSession>* session) { |
| ATRACE_CALL(); |
| if (offlineStreams.size() > 1) { |
| ALOGE("%s: more than one offline stream is not supported", __FUNCTION__); |
| return Status::ILLEGAL_ARGUMENT; |
| } |
| |
| if (msgs == nullptr || results == nullptr || info == nullptr || session == nullptr) { |
| ALOGE("%s: output arguments (%p, %p, %p, %p) must not be null", __FUNCTION__, |
| msgs, results, info, session); |
| return Status::ILLEGAL_ARGUMENT; |
| } |
| |
| msgs->clear(); |
| results->clear(); |
| |
| Mutex::Autolock _il(mInterfaceLock); |
| Status status = initStatus(); |
| if (status != Status::OK) { |
| return status; |
| } |
| |
| Mutex::Autolock _l(mLock); |
| for (auto streamId : offlineStreams) { |
| if (!supportOfflineLocked(streamId)) { |
| return Status::ILLEGAL_ARGUMENT; |
| } |
| } |
| |
| // pause output thread and get all remaining inflight requests |
| auto remainingReqs = mOutputThread->switchToOffline(); |
| std::vector<std::shared_ptr<V3_4::implementation::HalRequest>> halReqs; |
| |
| // Send out buffer/request error for remaining requests and filter requests |
| // to be handled in offline mode |
| for (auto& halReq : remainingReqs) { |
| bool dropReq = canDropRequest(offlineStreams, halReq); |
| if (dropReq) { |
| // Request is dropped completely. Just send request error and |
| // there is no need to send the request to offline session |
| processCaptureRequestError(halReq, msgs, results); |
| continue; |
| } |
| |
| // All requests reach here must have at least one offline stream output |
| NotifyMsg shutter; |
| shutter.type = MsgType::SHUTTER; |
| shutter.msg.shutter.frameNumber = halReq->frameNumber; |
| shutter.msg.shutter.timestamp = halReq->shutterTs; |
| msgs->push_back(shutter); |
| |
| std::vector<V3_4::implementation::HalStreamBuffer> offlineBuffers; |
| for (const auto& buffer : halReq->buffers) { |
| bool dropBuffer = true; |
| for (auto offlineStreamId : offlineStreams) { |
| if (buffer.streamId == offlineStreamId) { |
| dropBuffer = false; |
| break; |
| } |
| } |
| if (dropBuffer) { |
| NotifyMsg error; |
| error.type = MsgType::ERROR; |
| error.msg.error.frameNumber = halReq->frameNumber; |
| error.msg.error.errorStreamId = buffer.streamId; |
| error.msg.error.errorCode = ErrorCode::ERROR_BUFFER; |
| msgs->push_back(error); |
| |
| CaptureResult result; |
| result.frameNumber = halReq->frameNumber; |
| result.partialResult = 0; // buffer only result |
| result.inputBuffer.streamId = -1; |
| result.outputBuffers.resize(1); |
| result.outputBuffers[0].streamId = buffer.streamId; |
| result.outputBuffers[0].bufferId = buffer.bufferId; |
| result.outputBuffers[0].status = BufferStatus::ERROR; |
| if (buffer.acquireFence >= 0) { |
| native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0); |
| handle->data[0] = buffer.acquireFence; |
| result.outputBuffers[0].releaseFence.setTo(handle, /*shouldOwn*/false); |
| } |
| results->push_back(result); |
| } else { |
| offlineBuffers.push_back(buffer); |
| } |
| } |
| halReq->buffers = offlineBuffers; |
| halReqs.push_back(halReq); |
| } |
| |
| // convert hal requests to offline request |
| std::deque<std::shared_ptr<HalRequest>> offlineReqs(halReqs.size()); |
| size_t i = 0; |
| for (auto& v4lReq : halReqs) { |
| offlineReqs[i] = std::make_shared<HalRequest>(); |
| offlineReqs[i]->frameNumber = v4lReq->frameNumber; |
| offlineReqs[i]->setting = v4lReq->setting; |
| offlineReqs[i]->shutterTs = v4lReq->shutterTs; |
| offlineReqs[i]->buffers = v4lReq->buffers; |
| sp<V3_4::implementation::V4L2Frame> v4l2Frame = |
| static_cast<V3_4::implementation::V4L2Frame*>(v4lReq->frameIn.get()); |
| offlineReqs[i]->frameIn = new AllocatedV4L2Frame(v4l2Frame); |
| i++; |
| // enqueue V4L2 frame |
| enqueueV4l2Frame(v4l2Frame); |
| } |
| |
| // Collect buffer caches/streams |
| hidl_vec<Stream> streamInfos; |
| streamInfos.resize(offlineStreams.size()); |
| std::map<int, CirculatingBuffers> circulatingBuffers; |
| { |
| Mutex::Autolock _l(mCbsLock); |
| size_t idx = 0; |
| for(auto streamId : offlineStreams) { |
| circulatingBuffers[streamId] = mCirculatingBuffers.at(streamId); |
| mCirculatingBuffers.erase(streamId); |
| streamInfos[idx++] = mStreamMap.at(streamId); |
| mStreamMap.erase(streamId); |
| } |
| } |
| |
| fillOfflineSessionInfo(offlineStreams, offlineReqs, circulatingBuffers, info); |
| |
| // create the offline session object |
| bool afTrigger; |
| { |
| std::lock_guard<std::mutex> lk(mAfTriggerLock); |
| afTrigger = mAfTrigger; |
| } |
| sp<ExternalCameraOfflineSession> sessionImpl = new ExternalCameraOfflineSession( |
| mCroppingType, mCameraCharacteristics, mCameraId, |
| mExifMake, mExifModel, mBlobBufferSize, afTrigger, |
| streamInfos, offlineReqs, circulatingBuffers); |
| |
| bool initFailed = sessionImpl->initialize(); |
| if (initFailed) { |
| ALOGE("%s: offline session initialize failed!", __FUNCTION__); |
| return Status::INTERNAL_ERROR; |
| } |
| |
| // cleanup stream and buffer caches |
| { |
| Mutex::Autolock _l(mCbsLock); |
| for(auto pair : mStreamMap) { |
| cleanupBuffersLocked(/*Stream ID*/pair.first); |
| } |
| mCirculatingBuffers.clear(); |
| } |
| mStreamMap.clear(); |
| |
| // update inflight records |
| { |
| std::lock_guard<std::mutex> lk(mInflightFramesLock); |
| mInflightFrames.clear(); |
| } |
| |
| // stop v4l2 streaming |
| if (v4l2StreamOffLocked() !=0) { |
| ALOGE("%s: stop V4L2 streaming failed!", __FUNCTION__); |
| return Status::INTERNAL_ERROR; |
| } |
| |
| // No need to return session if there is no offline requests left |
| if (offlineReqs.size() != 0) { |
| *session = sessionImpl->getInterface(); |
| } else { |
| *session = nullptr; |
| } |
| return Status::OK; |
| } |
| |
| } // namespace implementation |
| } // namespace V3_6 |
| } // namespace device |
| } // namespace camera |
| } // namespace hardware |
| } // namespace android |