blob: 8f0231e33e742ac5e1ad3122b314d8af1d3d9774 [file] [log] [blame]
/*
* Copyright (C) 2020 Intel Corporation.
*
* 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 "ShareRefer"
#include "ShareReferBufferPool.h"
#include "PlatformData.h"
#include "iutils/CameraLog.h"
#include "iutils/Errors.h"
using std::map;
using std::unique_ptr;
using std::vector;
namespace icamera {
#define CONSUMER_BUFFER_NUM 2
int64_t ShareReferBufferPool::constructReferId(int32_t streamId, int32_t pgId, int32_t portId) {
return (((int64_t)streamId << 32) + ((int64_t)pgId << 16) + portId);
}
ShareReferBufferPool::~ShareReferBufferPool() {
AutoMutex l(mPairLock);
while (!mUserPairs.empty()) {
UserPair* pair = mUserPairs.back();
mUserPairs.pop_back();
delete pair;
}
}
int32_t ShareReferBufferPool::setReferPair(const std::string& producerPgName, int64_t producerId,
const std::string& consumerPgName, int64_t consumerId) {
CheckError(producerId == consumerId, BAD_VALUE, "same pair for producer/consumer %lx",
producerId);
UserPair* pair = new UserPair;
pair->producerPgName = producerPgName;
pair->producerId = producerId;
pair->consumerPgName = consumerPgName;
pair->consumerId = consumerId;
pair->busy = false;
LOG1("%s: %s:%lx -> %s:%lx", __func__, producerPgName.c_str(), producerId,
consumerPgName.c_str(), consumerId);
AutoMutex l(mPairLock);
mUserPairs.push_back(pair);
return OK;
}
int32_t ShareReferBufferPool::clearReferPair(int64_t id) {
AutoMutex l(mPairLock);
for (auto it = mUserPairs.begin(); it != mUserPairs.end(); it++) {
UserPair* pair = *it;
if (pair->producerId != id && pair->consumerId != id) continue;
pair->bufferLock.lock();
if (pair->busy) {
pair->bufferLock.unlock();
LOGE("Can't clear pair %lx because Q is busy!", id);
return UNKNOWN_ERROR;
}
mUserPairs.erase(it);
pair->bufferLock.unlock();
delete pair;
return OK;
}
return BAD_VALUE;
}
int32_t ShareReferBufferPool::getMinBufferNum(int64_t id) {
AutoMutex l(mPairLock);
for (auto pair : mUserPairs) {
if (pair->producerId == id)
return PlatformData::getMaxRawDataNum(mCameraId);
else if (pair->consumerId == id)
return CONSUMER_BUFFER_NUM;
}
return 0;
}
int32_t ShareReferBufferPool::registerReferBuffers(int64_t id, CIPR::Buffer* buffer) {
AutoMutex l(mPairLock);
UserPair* pair = findUserPair(id);
CheckError(!pair, UNKNOWN_ERROR, "Can't find id %lx", id);
ReferBuffer referBuf = {-1, buffer};
AutoMutex m(pair->bufferLock);
std::vector<ReferBuffer>& bufV =
(id == pair->producerId) ? pair->mProducerBuffers : pair->mConsumerBuffers;
bufV.push_back(referBuf);
return OK;
}
/**
* example for buffer operations:
* 1. sequence in buffer Q (len is 4)
* front->back: S6, S7, S8, S9
* 2. acquireBuffer before running for frame 10:
* front->back: (out),S7, S8, S9(in) (pop s6 as new output)
* 3. releaseBuffer after running:
* front->back: S7, S8, S9, S10 (push_back S10, new output)
*/
int32_t ShareReferBufferPool::acquireBuffer(int64_t id, CIPR::Buffer** referIn,
CIPR::Buffer** referOut, long outSequence) {
CheckError(!referIn || !referOut, BAD_VALUE, "nullptr input for refer buf pair");
long inSequence = outSequence - 1;
UserPair* pair = nullptr;
{
AutoMutex l(mPairLock);
pair = findUserPair(id);
CheckError(!pair, UNKNOWN_ERROR, "Can't find id %lx", id);
AutoMutex m(pair->bufferLock);
std::vector<ReferBuffer>& bufV =
(id == pair->producerId) ? pair->mProducerBuffers : pair->mConsumerBuffers;
CheckError(bufV.empty(), BAD_VALUE, "no refer buffer for id %lx", id);
*referOut = bufV.front().buffer;
bufV.erase(bufV.begin()); // pop front (the oldest one) as new output
*referIn = bufV.back().buffer;
if (bufV.back().sequence == inSequence || inSequence < 0) {
// Return if found required buffers or it is the 1st frame.
LOG2("%lx acquire in seq %ld, got %ld, out seq %ld", id, inSequence,
bufV.back().sequence, outSequence);
return OK;
} else if (id == pair->producerId) {
// Find required refer in buffer for producer.
// In general, it happens in reprocessing case that producer want to run old frame
for (auto& item : bufV) {
if (item.sequence == inSequence) {
*referIn = item.buffer;
LOG2("%lx acquire in seq %ld for reprocessing", id, inSequence);
return OK;
}
}
LOGE("%lx has no refer in seq %ld", id, inSequence);
return UNKNOWN_ERROR;
}
pair->busy = true;
}
LOG1("consumer %s try to get in seq %ld from %s", pair->consumerPgName.c_str(), inSequence,
pair->producerPgName.c_str());
CIPR::Buffer* srcBuf = nullptr;
int ret = NOT_ENOUGH_DATA;
int waitFrames = 3; // wait 3 frames
while (waitFrames-- && ret == NOT_ENOUGH_DATA) {
ConditionLock lock(pair->bufferLock);
ret = findReferBuffer(&pair->mProducerBuffers, inSequence, &srcBuf);
if (ret == NOT_ENOUGH_DATA) {
pair->bufferSignal.waitRelative(lock, kWaitDuration * SLOWLY_MULTIPLIER);
}
}
if (ret == OK && srcBuf) {
void* srcPtr = nullptr;
int32_t srcSize = 0;
srcBuf->getMemoryCpuPtr(&srcPtr);
srcBuf->getMemorySize(&srcSize);
void* dstPtr = nullptr;
int32_t dstSize = 0;
(*referIn)->getMemoryCpuPtr(&dstPtr);
(*referIn)->getMemorySize(&dstSize);
if (srcPtr && dstPtr) {
MEMCPY_S(dstPtr, dstSize, srcPtr, srcSize);
}
LOG1("%s acquire in seq %ld (copy from %s), out seq %ld", pair->consumerPgName.c_str(),
inSequence, pair->producerPgName.c_str(), outSequence);
}
AutoMutex m(pair->bufferLock);
pair->busy = false;
return ret;
}
int32_t ShareReferBufferPool::releaseBuffer(int64_t id, CIPR::Buffer* referIn,
CIPR::Buffer* referOut, long outSequence) {
CheckError(!referIn || !referOut, BAD_VALUE, "nullptr for refer buf pair for release");
AutoMutex l(mPairLock);
UserPair* pair = findUserPair(id);
CheckError(!pair, UNKNOWN_ERROR, "Can't find id %lx", id);
AutoMutex m(pair->bufferLock);
std::vector<ReferBuffer>& bufV =
(id == pair->producerId) ? pair->mProducerBuffers : pair->mConsumerBuffers;
if (outSequence < bufV.back().sequence) {
// Drop old data (in reprocessing case)
ReferBuffer referBuf = {-1, referOut};
bufV.insert(bufV.begin(), referBuf);
} else {
ReferBuffer referBuf = {outSequence, referOut};
bufV.push_back(referBuf);
}
pair->bufferSignal.signal();
return OK;
}
ShareReferBufferPool::UserPair* ShareReferBufferPool::findUserPair(int64_t id) {
for (auto pair : mUserPairs) {
if (pair->consumerId == id || pair->producerId == id) {
return pair;
}
}
return nullptr;
}
int ShareReferBufferPool::findReferBuffer(std::vector<ReferBuffer>* bufV, long sequence,
CIPR::Buffer** out) {
CheckError(!bufV, BAD_VALUE, "nullptr buffers");
CheckError(!out, BAD_VALUE, "nullptr out buffer");
if (bufV->empty() || bufV->back().sequence < sequence) return NOT_ENOUGH_DATA;
for (auto item = bufV->rbegin(); item != bufV->rend(); item++) {
if (item->sequence <= sequence) {
*out = item->buffer;
LOG2("%s: find seq %ld for required seq %ld", __func__, item->sequence, sequence);
return OK;
}
}
LOGE("No refer buffer with required seq %ld", sequence);
return UNKNOWN_ERROR;
}
} // namespace icamera