You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
361 lines
13 KiB
361 lines
13 KiB
/*
|
|
* 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
|