/* * Copyright (C) 2023 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_NDEBUG 0 #include "Buffer.h" #include #include namespace android { namespace webcam { BufferManager::BufferManager(BufferCreatorAndDestroyer* crD) : mCrD(crD) { if (crD == nullptr) { return; } std::vector> producerBuffers; if (mCrD->allocateAndMapBuffers(&mConsumerBufferItem.buffer, &producerBuffers) != Status::OK) { return; } mConsumerBufferItem.state = BufferState::FREE; for (auto& buf : producerBuffers) { mProducerBufferItems.emplace_back(buf, BufferState::FREE); } mInited = true; } BufferManager::~BufferManager() { std::vector> producerBuffers; for (auto& buf : mProducerBufferItems) { producerBuffers.push_back(buf.buffer); } mCrD->destroyBuffers(mConsumerBufferItem.buffer, producerBuffers); mConsumerBufferItem.buffer = nullptr; mProducerBufferItems.clear(); } Buffer* BufferManager::getFreeBufferIfAvailable() { // Producer call std::unique_lock l(mBufferLock); for (auto& bufferItem : mProducerBufferItems) { if (bufferItem.state == BufferState::FREE) { bufferItem.state = BufferState::IN_USE; return bufferItem.buffer.get(); } } for (const auto& bufferItem : mProducerBufferItems) { uint64_t bufferTs = bufferItem.buffer->getTimestamp(); ALOGV("%s: Buffer at state %d, ts %" PRIu64 ", v4l2 index %u", __FUNCTION__, (int)bufferItem.state, bufferTs, bufferItem.buffer->getIndex()); } return nullptr; } bool BufferManager::filledProducerBufferAvailableLocked(uint32_t* index) { uint32_t i = 0; bool found = false; uint32_t foundIndex = 0; uint64_t ts = 0; // Try to get the latest filled buffer. for (auto& bufferItem : mProducerBufferItems) { uint64_t bufferTs = bufferItem.buffer->getTimestamp(); ALOGV("%s: Buffer at index i %u : state %d, ts %" PRIu64 ", v4l2 index %u", __FUNCTION__, i, (int)bufferItem.state, bufferTs, bufferItem.buffer->getIndex()); if (bufferItem.state == BufferState::FILLED) { if (bufferTs > ts) { if (index != nullptr) { *index = i; } ts = bufferTs; found = true; foundIndex = i; } } i++; } // Actually cancel older buffers uint32_t j = 0; for (auto& bufferItem : mProducerBufferItems) { if (bufferItem.state == BufferState::FILLED && j != foundIndex) { bufferItem.state = BufferState::FREE; } j++; } return found; } Buffer* BufferManager::getFilledBufferAndSwap() { // Consumer call // Wait for a producer buffer item state to be FILLED // and swap consumer and producer buffer std::unique_lock l(mBufferLock); uint32_t index = 0; while (!filledProducerBufferAvailableLocked(&index)) { // TODO(b/267794640): Add a timeout to recover in case of a deadlock. // Wait till the producer side has filled the buffer. mProducerBufferFilled.wait(l); } // Mark it free so that the producer can start filling it. mConsumerBufferItem.state = BufferState::FREE; std::swap(mConsumerBufferItem, mProducerBufferItems[index]); // Now the consumer buffer is busy mConsumerBufferItem.state = BufferState::IN_USE; return mConsumerBufferItem.buffer.get(); } bool BufferManager::changeProducerBufferStateLocked(Buffer* buffer, BufferState newState) { bool found = false; uint32_t i = 0; for (auto& bufferItem : mProducerBufferItems) { if (buffer->getIndex() == bufferItem.buffer->getIndex()) { found = true; break; } i++; } if (!found) { ALOGE("%s queuing incorrect buffer, filled buffer index %u", __FUNCTION__, buffer->getIndex()); return false; } mProducerBufferItems[i].state = newState; return true; } Status BufferManager::cancelBuffer(Buffer* buffer) { std::unique_lock l(mBufferLock); if (!changeProducerBufferStateLocked(buffer, BufferState::FREE)) { return Status::ERROR; } return Status::OK; } Status BufferManager::queueFilledBuffer(Buffer* buffer) { std::unique_lock l(mBufferLock); if (!changeProducerBufferStateLocked(buffer, BufferState::FILLED)) { return Status::ERROR; } mProducerBufferFilled.notify_one(); return Status::OK; } } // namespace webcam } // namespace android