/* * Copyright (C) 2018 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.5" #include #include #include "ExternalCameraDeviceSession.h" namespace android { namespace hardware { namespace camera { namespace device { namespace V3_5 { namespace implementation { ExternalCameraDeviceSession::ExternalCameraDeviceSession( const sp& callback, const ExternalCameraConfig& cfg, const std::vector& sortedFormats, const CroppingType& croppingType, const common::V1_0::helper::CameraMetadata& chars, const std::string& cameraId, unique_fd v4l2Fd) : V3_4::implementation::ExternalCameraDeviceSession( callback, cfg, sortedFormats, croppingType, chars, cameraId, std::move(v4l2Fd)) { mCallback_3_5 = nullptr; auto castResult = V3_5::ICameraDeviceCallback::castFrom(callback); if (castResult.isOk()) { sp callback3_5 = castResult; if (callback3_5 != nullptr) { mCallback_3_5 = callback3_5; } } if (mCallback_3_5 != nullptr) { mSupportBufMgr = true; } } ExternalCameraDeviceSession::~ExternalCameraDeviceSession() { closeOutputThreadImpl(); } Return ExternalCameraDeviceSession::configureStreams_3_5( const StreamConfiguration& requestedConfiguration, ICameraDeviceSession::configureStreams_3_5_cb _hidl_cb) { return configureStreams_3_4(requestedConfiguration.v3_4, _hidl_cb); } Return ExternalCameraDeviceSession::signalStreamFlush( const hidl_vec& /*streamIds*/, uint32_t /*streamConfigCounter*/) { return Void(); } Status ExternalCameraDeviceSession::importRequestLocked( const CaptureRequest& request, hidl_vec& allBufPtrs, hidl_vec& allFences) { if (mSupportBufMgr) { return importRequestLockedImpl(request, allBufPtrs, allFences, /*allowEmptyBuf*/ true); } return importRequestLockedImpl(request, allBufPtrs, allFences, /*allowEmptyBuf*/ false); } ExternalCameraDeviceSession::BufferRequestThread::BufferRequestThread( wp parent, sp callbacks) : mParent(parent), mCallbacks(callbacks) {} int ExternalCameraDeviceSession::BufferRequestThread::requestBufferStart( const std::vector& bufReqs) { if (bufReqs.empty()) { ALOGE("%s: bufReqs is empty!", __FUNCTION__); return -1; } { std::lock_guard lk(mLock); if (mRequestingBuffer) { ALOGE("%s: BufferRequestThread does not support more than one concurrent request!", __FUNCTION__); return -1; } mBufferReqs = bufReqs; mRequestingBuffer = true; } mRequestCond.notify_one(); return 0; } int ExternalCameraDeviceSession::BufferRequestThread::waitForBufferRequestDone( std::vector* outBufReq) { std::unique_lock lk(mLock); if (!mRequestingBuffer) { ALOGE("%s: no pending buffer request!", __FUNCTION__); return -1; } if (mPendingReturnBufferReqs.empty()) { std::chrono::milliseconds timeout = std::chrono::milliseconds(kReqProcTimeoutMs); auto st = mRequestDoneCond.wait_for(lk, timeout); if (st == std::cv_status::timeout) { ALOGE("%s: wait for buffer request finish timeout!", __FUNCTION__); return -1; } } mRequestingBuffer = false; *outBufReq = std::move(mPendingReturnBufferReqs); mPendingReturnBufferReqs.clear(); return 0; } void ExternalCameraDeviceSession::BufferRequestThread::waitForNextRequest() { ATRACE_CALL(); std::unique_lock lk(mLock); int waitTimes = 0; while (mBufferReqs.empty()) { if (exitPending()) { return; } std::chrono::milliseconds timeout = std::chrono::milliseconds(kReqWaitTimeoutMs); auto st = mRequestCond.wait_for(lk, timeout); if (st == std::cv_status::timeout) { waitTimes++; if (waitTimes == kReqWaitTimesWarn) { // BufferRequestThread just wait forever for new buffer request // But it will print some periodic warning indicating it's waiting ALOGV("%s: still waiting for new buffer request", __FUNCTION__); waitTimes = 0; } } } // Fill in hidl BufferRequest mHalBufferReqs.resize(mBufferReqs.size()); for (size_t i = 0; i < mHalBufferReqs.size(); i++) { mHalBufferReqs[i].streamId = mBufferReqs[i].streamId; mHalBufferReqs[i].numBuffersRequested = 1; } } bool ExternalCameraDeviceSession::BufferRequestThread::threadLoop() { waitForNextRequest(); if (exitPending()) { return false; } ATRACE_BEGIN("HIDL requestStreamBuffers"); BufferRequestStatus status; hidl_vec bufRets; auto err = mCallbacks->requestStreamBuffers(mHalBufferReqs, [&status, &bufRets] (BufferRequestStatus s, const hidl_vec& rets) { status = s; bufRets = std::move(rets); }); ATRACE_END(); if (!err.isOk()) { ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str()); return false; } std::unique_lock lk(mLock); if (status == BufferRequestStatus::OK || status == BufferRequestStatus::FAILED_PARTIAL) { if (bufRets.size() != mHalBufferReqs.size()) { ALOGE("%s: expect %zu buffer requests returned, only got %zu", __FUNCTION__, mHalBufferReqs.size(), bufRets.size()); return false; } auto parent = mParent.promote(); if (parent == nullptr) { ALOGE("%s: session has been disconnected!", __FUNCTION__); return false; } hidl_vec importedFences; importedFences.resize(bufRets.size()); for (size_t i = 0; i < bufRets.size(); i++) { int streamId = bufRets[i].streamId; switch (bufRets[i].val.getDiscriminator()) { case StreamBuffersVal::hidl_discriminator::error: continue; case StreamBuffersVal::hidl_discriminator::buffers: { const hidl_vec& hBufs = bufRets[i].val.buffers(); if (hBufs.size() != 1) { ALOGE("%s: expect 1 buffer returned, got %zu!", __FUNCTION__, hBufs.size()); return false; } const V3_2::StreamBuffer& hBuf = hBufs[0]; mBufferReqs[i].bufferId = hBuf.bufferId; // TODO: create a batch import API so we don't need to lock/unlock mCbsLock // repeatedly? lk.unlock(); Status s = parent->importBuffer(streamId, hBuf.bufferId, hBuf.buffer.getNativeHandle(), /*out*/&mBufferReqs[i].bufPtr, /*allowEmptyBuf*/false); lk.lock(); if (s != Status::OK) { ALOGE("%s: stream %d import buffer failed!", __FUNCTION__, streamId); cleanupInflightFences(importedFences, i - 1); return false; } if (!sHandleImporter.importFence(hBuf.acquireFence, mBufferReqs[i].acquireFence)) { ALOGE("%s: stream %d import fence failed!", __FUNCTION__, streamId); cleanupInflightFences(importedFences, i - 1); return false; } importedFences[i] = mBufferReqs[i].acquireFence; } break; default: ALOGE("%s: unkown StreamBuffersVal discrimator!", __FUNCTION__); return false; } } } else { ALOGE("%s: requestStreamBuffers call failed!", __FUNCTION__); } mPendingReturnBufferReqs = std::move(mBufferReqs); mBufferReqs.clear(); lk.unlock(); mRequestDoneCond.notify_one(); return true; } void ExternalCameraDeviceSession::initOutputThread() { if (mSupportBufMgr) { mBufferRequestThread = new BufferRequestThread(this, mCallback_3_5); mBufferRequestThread->run("ExtCamBufReq", PRIORITY_DISPLAY); } mOutputThread = new OutputThread( this, mCroppingType, mCameraCharacteristics, mBufferRequestThread); } void ExternalCameraDeviceSession::closeOutputThreadImpl() { if (mBufferRequestThread) { mBufferRequestThread->requestExit(); mBufferRequestThread->join(); mBufferRequestThread.clear(); } } void ExternalCameraDeviceSession::closeOutputThread() { closeOutputThreadImpl(); V3_4::implementation::ExternalCameraDeviceSession::closeOutputThread(); } ExternalCameraDeviceSession::OutputThread::OutputThread( wp parent, CroppingType ct, const common::V1_0::helper::CameraMetadata& chars, sp bufReqThread) : V3_4::implementation::ExternalCameraDeviceSession::OutputThread(parent, ct, chars), mBufferRequestThread(bufReqThread) {} ExternalCameraDeviceSession::OutputThread::~OutputThread() {} int ExternalCameraDeviceSession::OutputThread::requestBufferStart( const std::vector& bufs) { if (mBufferRequestThread != nullptr) { return mBufferRequestThread->requestBufferStart(bufs); } return 0; } int ExternalCameraDeviceSession::OutputThread::waitForBufferRequestDone( /*out*/std::vector* outBufs) { if (mBufferRequestThread != nullptr) { return mBufferRequestThread->waitForBufferRequestDone(outBufs); } return 0; } Return ExternalCameraDeviceSession::isReconfigurationRequired( const V3_2::CameraMetadata& /*oldSessionParams*/, const V3_2::CameraMetadata& /*newSessionParams*/, ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb) { //Stub implementation _hidl_cb(Status::OK, true); return Void(); } } // namespace implementation } // namespace V3_5 } // namespace device } // namespace camera } // namespace hardware } // namespace android