The FrameBuffer used to track any addition to the request in an under-run event was shadowed against the returned buffer, being placed back on the availableBuffers_ queue. Rename the shadowed variable to be more explicit that it is a separate Buffer. Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
237 lines
4.9 KiB
C++
237 lines
4.9 KiB
C++
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
/*
|
|
* Copyright (C) 2020, Raspberry Pi (Trading) Ltd.
|
|
*
|
|
* rpi_stream.cpp - Raspberry Pi device stream abstraction class.
|
|
*/
|
|
#include "rpi_stream.h"
|
|
|
|
#include "libcamera/internal/log.h"
|
|
|
|
namespace libcamera {
|
|
|
|
LOG_DEFINE_CATEGORY(RPISTREAM)
|
|
|
|
namespace RPi {
|
|
|
|
V4L2VideoDevice *Stream::dev() const
|
|
{
|
|
return dev_.get();
|
|
}
|
|
|
|
std::string Stream::name() const
|
|
{
|
|
return name_;
|
|
}
|
|
|
|
void Stream::reset()
|
|
{
|
|
external_ = false;
|
|
clearBuffers();
|
|
}
|
|
|
|
void Stream::setExternal(bool external)
|
|
{
|
|
/* Import streams cannot be external. */
|
|
ASSERT(!external || !importOnly_);
|
|
external_ = external;
|
|
}
|
|
|
|
bool Stream::isExternal() const
|
|
{
|
|
return external_;
|
|
}
|
|
|
|
void Stream::setExportedBuffers(std::vector<std::unique_ptr<FrameBuffer>> *buffers)
|
|
{
|
|
for (auto const &buffer : *buffers)
|
|
bufferMap_.emplace(id_.get(), buffer.get());
|
|
}
|
|
|
|
const BufferMap &Stream::getBuffers() const
|
|
{
|
|
return bufferMap_;
|
|
}
|
|
|
|
int Stream::getBufferId(FrameBuffer *buffer) const
|
|
{
|
|
if (importOnly_)
|
|
return -1;
|
|
|
|
/* Find the buffer in the map, and return the buffer id. */
|
|
auto it = std::find_if(bufferMap_.begin(), bufferMap_.end(),
|
|
[&buffer](auto const &p) { return p.second == buffer; });
|
|
|
|
if (it == bufferMap_.end())
|
|
return -1;
|
|
|
|
return it->first;
|
|
}
|
|
|
|
void Stream::setExternalBuffer(FrameBuffer *buffer)
|
|
{
|
|
bufferMap_.emplace(RPi::BufferMask::EXTERNAL_BUFFER | id_.get(), buffer);
|
|
}
|
|
|
|
void Stream::removeExternalBuffer(FrameBuffer *buffer)
|
|
{
|
|
int id = getBufferId(buffer);
|
|
|
|
/* Ensure we have this buffer in the stream, and it is marked external. */
|
|
ASSERT(id != -1 && (id & RPi::BufferMask::EXTERNAL_BUFFER));
|
|
bufferMap_.erase(id);
|
|
}
|
|
|
|
int Stream::prepareBuffers(unsigned int count)
|
|
{
|
|
int ret;
|
|
|
|
if (!importOnly_) {
|
|
if (count) {
|
|
/* Export some frame buffers for internal use. */
|
|
ret = dev_->exportBuffers(count, &internalBuffers_);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Add these exported buffers to the internal/external buffer list. */
|
|
setExportedBuffers(&internalBuffers_);
|
|
|
|
/* Add these buffers to the queue of internal usable buffers. */
|
|
for (auto const &buffer : internalBuffers_)
|
|
availableBuffers_.push(buffer.get());
|
|
}
|
|
|
|
/* We must import all internal/external exported buffers. */
|
|
count = bufferMap_.size();
|
|
}
|
|
|
|
return dev_->importBuffers(count);
|
|
}
|
|
|
|
int Stream::queueBuffer(FrameBuffer *buffer)
|
|
{
|
|
/*
|
|
* A nullptr buffer implies an external stream, but no external
|
|
* buffer has been supplied in the Request. So, pick one from the
|
|
* availableBuffers_ queue.
|
|
*/
|
|
if (!buffer) {
|
|
if (availableBuffers_.empty()) {
|
|
LOG(RPISTREAM, Info) << "No buffers available for "
|
|
<< name_;
|
|
/*
|
|
* Note that we need to queue an internal buffer as soon
|
|
* as one becomes available.
|
|
*/
|
|
requestBuffers_.push(nullptr);
|
|
return 0;
|
|
}
|
|
|
|
buffer = availableBuffers_.front();
|
|
availableBuffers_.pop();
|
|
}
|
|
|
|
/*
|
|
* If no earlier requests are pending to be queued we can go ahead and
|
|
* queue this buffer into the device.
|
|
*/
|
|
if (requestBuffers_.empty())
|
|
return queueToDevice(buffer);
|
|
|
|
/*
|
|
* There are earlier Request buffers to be queued, so this buffer must go
|
|
* on the waiting list.
|
|
*/
|
|
requestBuffers_.push(buffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Stream::returnBuffer(FrameBuffer *buffer)
|
|
{
|
|
/* This can only be called for external streams. */
|
|
ASSERT(external_);
|
|
|
|
/* Push this buffer back into the queue to be used again. */
|
|
availableBuffers_.push(buffer);
|
|
|
|
/* Allow the buffer id to be reused. */
|
|
id_.release(getBufferId(buffer));
|
|
|
|
/*
|
|
* Do we have any Request buffers that are waiting to be queued?
|
|
* If so, do it now as availableBuffers_ will not be empty.
|
|
*/
|
|
while (!requestBuffers_.empty()) {
|
|
FrameBuffer *requestBuffer = requestBuffers_.front();
|
|
|
|
if (!requestBuffer) {
|
|
/*
|
|
* We want to queue an internal buffer, but none
|
|
* are available. Can't do anything, quit the loop.
|
|
*/
|
|
if (availableBuffers_.empty())
|
|
break;
|
|
|
|
/*
|
|
* We want to queue an internal buffer, and at least one
|
|
* is available.
|
|
*/
|
|
requestBuffer = availableBuffers_.front();
|
|
availableBuffers_.pop();
|
|
}
|
|
|
|
requestBuffers_.pop();
|
|
queueToDevice(requestBuffer);
|
|
}
|
|
}
|
|
|
|
int Stream::queueAllBuffers()
|
|
{
|
|
int ret;
|
|
|
|
if (external_)
|
|
return 0;
|
|
|
|
while (!availableBuffers_.empty()) {
|
|
ret = queueBuffer(availableBuffers_.front());
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
availableBuffers_.pop();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Stream::releaseBuffers()
|
|
{
|
|
dev_->releaseBuffers();
|
|
clearBuffers();
|
|
}
|
|
|
|
void Stream::clearBuffers()
|
|
{
|
|
availableBuffers_ = std::queue<FrameBuffer *>{};
|
|
requestBuffers_ = std::queue<FrameBuffer *>{};
|
|
internalBuffers_.clear();
|
|
bufferMap_.clear();
|
|
id_.reset();
|
|
}
|
|
|
|
int Stream::queueToDevice(FrameBuffer *buffer)
|
|
{
|
|
LOG(RPISTREAM, Debug) << "Queuing buffer " << getBufferId(buffer)
|
|
<< " for " << name_;
|
|
|
|
int ret = dev_->queueBuffer(buffer);
|
|
if (ret)
|
|
LOG(RPISTREAM, Error) << "Failed to queue buffer for "
|
|
<< name_;
|
|
return ret;
|
|
}
|
|
|
|
} /* namespace RPi */
|
|
|
|
} /* namespace libcamera */
|