Yin-Chia Yeh | 4da7c6c | 2020-01-16 17:06:36 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2020 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #define LOG_TAG "ExtCamDevSsn@3.6" |
| 18 | #include <android/log.h> |
| 19 | |
| 20 | #include <utils/Trace.h> |
| 21 | #include "ExternalCameraDeviceSession.h" |
| 22 | |
| 23 | namespace android { |
| 24 | namespace hardware { |
| 25 | namespace camera { |
| 26 | namespace device { |
| 27 | namespace V3_6 { |
| 28 | namespace implementation { |
| 29 | |
| 30 | ExternalCameraDeviceSession::ExternalCameraDeviceSession( |
| 31 | const sp<V3_2::ICameraDeviceCallback>& callback, |
| 32 | const ExternalCameraConfig& cfg, |
| 33 | const std::vector<SupportedV4L2Format>& sortedFormats, |
| 34 | const CroppingType& croppingType, |
| 35 | const common::V1_0::helper::CameraMetadata& chars, |
| 36 | const std::string& cameraId, |
| 37 | unique_fd v4l2Fd) : |
| 38 | V3_5::implementation::ExternalCameraDeviceSession( |
| 39 | callback, cfg, sortedFormats, croppingType, chars, cameraId, std::move(v4l2Fd)) { |
| 40 | } |
| 41 | |
| 42 | ExternalCameraDeviceSession::~ExternalCameraDeviceSession() {} |
| 43 | |
| 44 | |
| 45 | Return<void> ExternalCameraDeviceSession::configureStreams_3_6( |
| 46 | const StreamConfiguration& requestedConfiguration, |
| 47 | ICameraDeviceSession::configureStreams_3_6_cb _hidl_cb) { |
| 48 | V3_2::StreamConfiguration config_v32; |
| 49 | V3_3::HalStreamConfiguration outStreams_v33; |
| 50 | V3_6::HalStreamConfiguration outStreams; |
| 51 | const V3_4::StreamConfiguration& requestedConfiguration_3_4 = requestedConfiguration.v3_4; |
| 52 | Mutex::Autolock _il(mInterfaceLock); |
| 53 | |
| 54 | config_v32.operationMode = requestedConfiguration_3_4.operationMode; |
| 55 | config_v32.streams.resize(requestedConfiguration_3_4.streams.size()); |
| 56 | uint32_t blobBufferSize = 0; |
| 57 | int numStallStream = 0; |
| 58 | for (size_t i = 0; i < config_v32.streams.size(); i++) { |
| 59 | config_v32.streams[i] = requestedConfiguration_3_4.streams[i].v3_2; |
| 60 | if (config_v32.streams[i].format == PixelFormat::BLOB) { |
| 61 | blobBufferSize = requestedConfiguration_3_4.streams[i].bufferSize; |
| 62 | numStallStream++; |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | // Fail early if there are multiple BLOB streams |
| 67 | if (numStallStream > kMaxStallStream) { |
| 68 | ALOGE("%s: too many stall streams (expect <= %d, got %d)", __FUNCTION__, |
| 69 | kMaxStallStream, numStallStream); |
| 70 | _hidl_cb(Status::ILLEGAL_ARGUMENT, outStreams); |
| 71 | return Void(); |
| 72 | } |
| 73 | |
| 74 | Status status = configureStreams(config_v32, &outStreams_v33, blobBufferSize); |
| 75 | |
Yin-Chia Yeh | 5dab728 | 2020-01-21 10:08:12 -0800 | [diff] [blame] | 76 | fillOutputStream3_6(outStreams_v33, &outStreams); |
| 77 | |
Yin-Chia Yeh | 4da7c6c | 2020-01-16 17:06:36 -0800 | [diff] [blame] | 78 | _hidl_cb(status, outStreams); |
| 79 | return Void(); |
| 80 | } |
| 81 | |
| 82 | Return<void> ExternalCameraDeviceSession::switchToOffline( |
| 83 | const hidl_vec<int32_t>& streamsToKeep, |
| 84 | ICameraDeviceSession::switchToOffline_cb _hidl_cb) { |
Yin-Chia Yeh | 5dab728 | 2020-01-21 10:08:12 -0800 | [diff] [blame] | 85 | std::vector<NotifyMsg> msgs; |
| 86 | std::vector<CaptureResult> results; |
| 87 | CameraOfflineSessionInfo info; |
| 88 | sp<ICameraOfflineSession> session; |
| 89 | |
| 90 | Status st = switchToOffline(streamsToKeep, &msgs, &results, &info, &session); |
| 91 | |
| 92 | mCallback->notify(msgs); |
| 93 | hidl_vec<CaptureResult> hidlResults(std::move(results)); |
| 94 | invokeProcessCaptureResultCallback(hidlResults, /* tryWriteFmq */true); |
| 95 | V3_4::implementation::freeReleaseFences(hidlResults); |
| 96 | |
| 97 | _hidl_cb(st, info, session); |
Yin-Chia Yeh | 4da7c6c | 2020-01-16 17:06:36 -0800 | [diff] [blame] | 98 | return Void(); |
| 99 | } |
| 100 | |
Yin-Chia Yeh | 5dab728 | 2020-01-21 10:08:12 -0800 | [diff] [blame] | 101 | void ExternalCameraDeviceSession::fillOutputStream3_6( |
| 102 | const V3_3::HalStreamConfiguration& outStreams_v33, |
| 103 | /*out*/V3_6::HalStreamConfiguration* outStreams_v36) { |
| 104 | if (outStreams_v36 == nullptr) { |
| 105 | ALOGE("%s: outStreams_v36 must not be null!", __FUNCTION__); |
| 106 | return; |
| 107 | } |
| 108 | Mutex::Autolock _l(mLock); |
| 109 | outStreams_v36->streams.resize(outStreams_v33.streams.size()); |
| 110 | for (size_t i = 0; i < outStreams_v36->streams.size(); i++) { |
| 111 | outStreams_v36->streams[i].v3_4.v3_3 = outStreams_v33.streams[i]; |
| 112 | outStreams_v36->streams[i].supportOffline = |
| 113 | supportOfflineLocked(outStreams_v33.streams[i].v3_2.id); |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | bool ExternalCameraDeviceSession::supportOfflineLocked(int32_t streamId) { |
| 118 | const Stream& stream = mStreamMap[streamId]; |
| 119 | if (stream.format == PixelFormat::BLOB && |
| 120 | stream.dataSpace == static_cast<int32_t>(Dataspace::V0_JFIF)) { |
| 121 | return true; |
| 122 | } |
| 123 | // TODO: support YUV output stream? |
| 124 | return false; |
| 125 | } |
| 126 | |
| 127 | bool ExternalCameraDeviceSession::canDropRequest(const hidl_vec<int32_t>& offlineStreams, |
| 128 | std::shared_ptr<V3_4::implementation::HalRequest> halReq) { |
| 129 | for (const auto& buffer : halReq->buffers) { |
| 130 | for (auto offlineStreamId : offlineStreams) { |
| 131 | if (buffer.streamId == offlineStreamId) { |
| 132 | return false; |
| 133 | } |
| 134 | } |
| 135 | } |
| 136 | // Only drop a request completely if it has no offline output |
| 137 | return true; |
| 138 | } |
| 139 | |
| 140 | void ExternalCameraDeviceSession::fillOfflineSessionInfo(const hidl_vec<int32_t>& offlineStreams, |
| 141 | std::deque<std::shared_ptr<HalRequest>>& offlineReqs, |
| 142 | const std::map<int, CirculatingBuffers>& circulatingBuffers, |
| 143 | /*out*/CameraOfflineSessionInfo* info) { |
| 144 | if (info == nullptr) { |
| 145 | ALOGE("%s: output info must not be null!", __FUNCTION__); |
| 146 | return; |
| 147 | } |
| 148 | |
| 149 | info->offlineStreams.resize(offlineStreams.size()); |
| 150 | info->offlineRequests.resize(offlineReqs.size()); |
| 151 | |
Yin-Chia Yeh | 5dab728 | 2020-01-21 10:08:12 -0800 | [diff] [blame] | 152 | // Fill in offline reqs and count outstanding buffers |
| 153 | for (size_t i = 0; i < offlineReqs.size(); i++) { |
| 154 | info->offlineRequests[i].frameNumber = offlineReqs[i]->frameNumber; |
| 155 | info->offlineRequests[i].pendingStreams.resize(offlineReqs[i]->buffers.size()); |
| 156 | for (size_t bIdx = 0; bIdx < offlineReqs[i]->buffers.size(); bIdx++) { |
| 157 | int32_t streamId = offlineReqs[i]->buffers[bIdx].streamId; |
| 158 | info->offlineRequests[i].pendingStreams[bIdx] = streamId; |
Yin-Chia Yeh | 5dab728 | 2020-01-21 10:08:12 -0800 | [diff] [blame] | 159 | } |
| 160 | } |
| 161 | |
| 162 | for (size_t i = 0; i < offlineStreams.size(); i++) { |
| 163 | int32_t streamId = offlineStreams[i]; |
| 164 | info->offlineStreams[i].id = streamId; |
Yin-Chia Yeh | 97978fb | 2020-01-25 18:15:00 -0800 | [diff] [blame] | 165 | // outstanding buffers are 0 since we are doing hal buffer management and |
| 166 | // offline session will ask for those buffers later |
| 167 | info->offlineStreams[i].numOutstandingBuffers = 0; |
Yin-Chia Yeh | 5dab728 | 2020-01-21 10:08:12 -0800 | [diff] [blame] | 168 | const CirculatingBuffers& bufIdMap = circulatingBuffers.at(streamId); |
| 169 | info->offlineStreams[i].circulatingBufferIds.resize(bufIdMap.size()); |
| 170 | size_t bIdx = 0; |
| 171 | for (const auto& pair : bufIdMap) { |
| 172 | // Fill in bufferId |
| 173 | info->offlineStreams[i].circulatingBufferIds[bIdx++] = pair.first; |
| 174 | } |
| 175 | |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | Status ExternalCameraDeviceSession::switchToOffline(const hidl_vec<int32_t>& offlineStreams, |
| 180 | /*out*/std::vector<NotifyMsg>* msgs, |
| 181 | /*out*/std::vector<CaptureResult>* results, |
| 182 | /*out*/CameraOfflineSessionInfo* info, |
| 183 | /*out*/sp<ICameraOfflineSession>* session) { |
| 184 | ATRACE_CALL(); |
| 185 | if (offlineStreams.size() > 1) { |
| 186 | ALOGE("%s: more than one offline stream is not supported", __FUNCTION__); |
| 187 | return Status::ILLEGAL_ARGUMENT; |
| 188 | } |
| 189 | |
| 190 | if (msgs == nullptr || results == nullptr || info == nullptr || session == nullptr) { |
| 191 | ALOGE("%s: output arguments (%p, %p, %p, %p) must not be null", __FUNCTION__, |
| 192 | msgs, results, info, session); |
| 193 | return Status::ILLEGAL_ARGUMENT; |
| 194 | } |
| 195 | |
| 196 | msgs->clear(); |
| 197 | results->clear(); |
| 198 | |
| 199 | Mutex::Autolock _il(mInterfaceLock); |
| 200 | Status status = initStatus(); |
| 201 | if (status != Status::OK) { |
| 202 | return status; |
| 203 | } |
| 204 | |
| 205 | Mutex::Autolock _l(mLock); |
| 206 | for (auto streamId : offlineStreams) { |
| 207 | if (!supportOfflineLocked(streamId)) { |
| 208 | return Status::ILLEGAL_ARGUMENT; |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | // pause output thread and get all remaining inflight requests |
| 213 | auto remainingReqs = mOutputThread->switchToOffline(); |
| 214 | std::vector<std::shared_ptr<V3_4::implementation::HalRequest>> halReqs; |
| 215 | |
| 216 | // Send out buffer/request error for remaining requests and filter requests |
| 217 | // to be handled in offline mode |
| 218 | for (auto& halReq : remainingReqs) { |
| 219 | bool dropReq = canDropRequest(offlineStreams, halReq); |
| 220 | if (dropReq) { |
| 221 | // Request is dropped completely. Just send request error and |
| 222 | // there is no need to send the request to offline session |
| 223 | processCaptureRequestError(halReq, msgs, results); |
| 224 | continue; |
| 225 | } |
| 226 | |
| 227 | // All requests reach here must have at least one offline stream output |
| 228 | NotifyMsg shutter; |
| 229 | shutter.type = MsgType::SHUTTER; |
| 230 | shutter.msg.shutter.frameNumber = halReq->frameNumber; |
| 231 | shutter.msg.shutter.timestamp = halReq->shutterTs; |
| 232 | msgs->push_back(shutter); |
| 233 | |
| 234 | std::vector<V3_4::implementation::HalStreamBuffer> offlineBuffers; |
| 235 | for (const auto& buffer : halReq->buffers) { |
| 236 | bool dropBuffer = true; |
| 237 | for (auto offlineStreamId : offlineStreams) { |
| 238 | if (buffer.streamId == offlineStreamId) { |
| 239 | dropBuffer = false; |
| 240 | break; |
| 241 | } |
| 242 | } |
| 243 | if (dropBuffer) { |
| 244 | NotifyMsg error; |
| 245 | error.type = MsgType::ERROR; |
| 246 | error.msg.error.frameNumber = halReq->frameNumber; |
| 247 | error.msg.error.errorStreamId = buffer.streamId; |
| 248 | error.msg.error.errorCode = ErrorCode::ERROR_BUFFER; |
| 249 | msgs->push_back(error); |
| 250 | |
| 251 | CaptureResult result; |
| 252 | result.frameNumber = halReq->frameNumber; |
| 253 | result.partialResult = 0; // buffer only result |
| 254 | result.inputBuffer.streamId = -1; |
| 255 | result.outputBuffers.resize(1); |
| 256 | result.outputBuffers[0].streamId = buffer.streamId; |
| 257 | result.outputBuffers[0].bufferId = buffer.bufferId; |
| 258 | result.outputBuffers[0].status = BufferStatus::ERROR; |
| 259 | if (buffer.acquireFence >= 0) { |
| 260 | native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0); |
| 261 | handle->data[0] = buffer.acquireFence; |
| 262 | result.outputBuffers[0].releaseFence.setTo(handle, /*shouldOwn*/false); |
| 263 | } |
| 264 | results->push_back(result); |
| 265 | } else { |
| 266 | offlineBuffers.push_back(buffer); |
| 267 | } |
| 268 | } |
| 269 | halReq->buffers = offlineBuffers; |
| 270 | halReqs.push_back(halReq); |
| 271 | } |
| 272 | |
| 273 | // convert hal requests to offline request |
| 274 | std::deque<std::shared_ptr<HalRequest>> offlineReqs(halReqs.size()); |
Emilian Peev | 36aa827 | 2020-02-26 16:31:47 -0800 | [diff] [blame] | 275 | size_t i = 0; |
Yin-Chia Yeh | 5dab728 | 2020-01-21 10:08:12 -0800 | [diff] [blame] | 276 | for (auto& v4lReq : halReqs) { |
Emilian Peev | 36aa827 | 2020-02-26 16:31:47 -0800 | [diff] [blame] | 277 | offlineReqs[i] = std::make_shared<HalRequest>(); |
| 278 | offlineReqs[i]->frameNumber = v4lReq->frameNumber; |
| 279 | offlineReqs[i]->setting = v4lReq->setting; |
| 280 | offlineReqs[i]->shutterTs = v4lReq->shutterTs; |
| 281 | offlineReqs[i]->buffers = v4lReq->buffers; |
Yin-Chia Yeh | 5dab728 | 2020-01-21 10:08:12 -0800 | [diff] [blame] | 282 | sp<V3_4::implementation::V4L2Frame> v4l2Frame = |
| 283 | static_cast<V3_4::implementation::V4L2Frame*>(v4lReq->frameIn.get()); |
Emilian Peev | 36aa827 | 2020-02-26 16:31:47 -0800 | [diff] [blame] | 284 | offlineReqs[i]->frameIn = new AllocatedV4L2Frame(v4l2Frame); |
| 285 | i++; |
Yin-Chia Yeh | 5dab728 | 2020-01-21 10:08:12 -0800 | [diff] [blame] | 286 | // enqueue V4L2 frame |
| 287 | enqueueV4l2Frame(v4l2Frame); |
| 288 | } |
| 289 | |
| 290 | // Collect buffer caches/streams |
| 291 | hidl_vec<Stream> streamInfos; |
| 292 | streamInfos.resize(offlineStreams.size()); |
| 293 | std::map<int, CirculatingBuffers> circulatingBuffers; |
| 294 | { |
| 295 | Mutex::Autolock _l(mCbsLock); |
| 296 | size_t idx = 0; |
| 297 | for(auto streamId : offlineStreams) { |
| 298 | circulatingBuffers[streamId] = mCirculatingBuffers.at(streamId); |
| 299 | mCirculatingBuffers.erase(streamId); |
| 300 | streamInfos[idx++] = mStreamMap.at(streamId); |
| 301 | mStreamMap.erase(streamId); |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | fillOfflineSessionInfo(offlineStreams, offlineReqs, circulatingBuffers, info); |
| 306 | |
| 307 | // create the offline session object |
| 308 | bool afTrigger; |
| 309 | { |
| 310 | std::lock_guard<std::mutex> lk(mAfTriggerLock); |
| 311 | afTrigger = mAfTrigger; |
| 312 | } |
| 313 | sp<ExternalCameraOfflineSession> sessionImpl = new ExternalCameraOfflineSession( |
| 314 | mCroppingType, mCameraCharacteristics, mCameraId, |
| 315 | mExifMake, mExifModel, mBlobBufferSize, afTrigger, |
| 316 | streamInfos, offlineReqs, circulatingBuffers); |
| 317 | |
| 318 | bool initFailed = sessionImpl->initialize(); |
| 319 | if (initFailed) { |
| 320 | ALOGE("%s: offline session initialize failed!", __FUNCTION__); |
| 321 | return Status::INTERNAL_ERROR; |
| 322 | } |
| 323 | |
| 324 | // cleanup stream and buffer caches |
| 325 | { |
| 326 | Mutex::Autolock _l(mCbsLock); |
| 327 | for(auto pair : mStreamMap) { |
| 328 | cleanupBuffersLocked(/*Stream ID*/pair.first); |
| 329 | } |
| 330 | mCirculatingBuffers.clear(); |
| 331 | } |
| 332 | mStreamMap.clear(); |
| 333 | |
| 334 | // update inflight records |
| 335 | { |
| 336 | std::lock_guard<std::mutex> lk(mInflightFramesLock); |
| 337 | mInflightFrames.clear(); |
| 338 | } |
| 339 | |
| 340 | // stop v4l2 streaming |
| 341 | if (v4l2StreamOffLocked() !=0) { |
| 342 | ALOGE("%s: stop V4L2 streaming failed!", __FUNCTION__); |
| 343 | return Status::INTERNAL_ERROR; |
| 344 | } |
| 345 | |
Yin-Chia Yeh | 97978fb | 2020-01-25 18:15:00 -0800 | [diff] [blame] | 346 | // No need to return session if there is no offline requests left |
| 347 | if (offlineReqs.size() != 0) { |
| 348 | *session = sessionImpl->getInterface(); |
| 349 | } else { |
| 350 | *session = nullptr; |
| 351 | } |
Yin-Chia Yeh | 5dab728 | 2020-01-21 10:08:12 -0800 | [diff] [blame] | 352 | return Status::OK; |
| 353 | } |
| 354 | |
Yin-Chia Yeh | 4da7c6c | 2020-01-16 17:06:36 -0800 | [diff] [blame] | 355 | } // namespace implementation |
| 356 | } // namespace V3_6 |
| 357 | } // namespace device |
| 358 | } // namespace camera |
| 359 | } // namespace hardware |
| 360 | } // namespace android |