libcamera: pipeline: raspberrypi: Rework stream buffer logic for zero-copy

Stop using v4l2_videodevice::allocateBuffer() for internal buffers and
instead export/import all buffers. This allows the pipeline to return
any stream buffer requested by the application as zero-copy.

Advertise the Unicam Image stream as the RAW capture stream now.

The RPiStream object now maintains a new list of buffers that are
available to queue into a device. This is needed to distinguish between
FrameBuffers allocated for internal use vs externally provided buffers.
When a Request comes in, if a buffer is not provided for an exported
stream, we re-use a buffer from this list.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Tested-by: David Plowman <david.plowman@raspberrypi.com>
Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
This commit is contained in:
Naushir Patuck
2020-09-18 10:42:27 +01:00
committed by Niklas Söderlund
parent 3e7ee080d6
commit 2df7bf1681
3 changed files with 237 additions and 153 deletions

View File

@@ -27,78 +27,120 @@ std::string RPiStream::name() const
void RPiStream::reset()
{
external_ = false;
internalBuffers_.clear();
}
bool RPiStream::isImporter() const
{
return importOnly_;
clearBuffers();
}
void RPiStream::setExternal(bool external)
{
/* Import streams cannot be external. */
ASSERT(!external || !importOnly_);
external_ = external;
}
bool RPiStream::isExternal() const
{
/*
* Import streams cannot be external.
*
* RAW capture is a special case where we simply copy the RAW
* buffer out of the request. All other buffer handling happens
* as if the stream is internal.
*/
return external_ && !importOnly_;
return external_;
}
void RPiStream::setExternalBuffers(std::vector<std::unique_ptr<FrameBuffer>> *buffers)
void RPiStream::setExportedBuffers(std::vector<std::unique_ptr<FrameBuffer>> *buffers)
{
externalBuffers_ = buffers;
std::transform(buffers->begin(), buffers->end(), std::back_inserter(bufferList_),
[](std::unique_ptr<FrameBuffer> &b) { return b.get(); });
}
const std::vector<std::unique_ptr<FrameBuffer>> *RPiStream::getBuffers() const
const std::vector<FrameBuffer *> &RPiStream::getBuffers() const
{
return external_ ? externalBuffers_ : &internalBuffers_;
return bufferList_;
}
bool RPiStream::findFrameBuffer(FrameBuffer *buffer) const
{
auto start = external_ ? externalBuffers_->begin() : internalBuffers_.begin();
auto end = external_ ? externalBuffers_->end() : internalBuffers_.end();
if (importOnly_)
return false;
if (std::find_if(start, end,
[buffer](std::unique_ptr<FrameBuffer> const &ref) { return ref.get() == buffer; }) != end)
if (std::find(bufferList_.begin(), bufferList_.end(), buffer) != bufferList_.end())
return true;
return false;
}
int RPiStream::importBuffers(unsigned int count)
int RPiStream::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 = bufferList_.size();
}
return dev_->importBuffers(count);
}
int RPiStream::allocateBuffers(unsigned int count)
int RPiStream::queueBuffer(FrameBuffer *buffer)
{
return dev_->allocateBuffers(count, &internalBuffers_);
/*
* A nullptr buffer implies an external stream, but no external
* buffer has been supplied. So, pick one from the availableBuffers_
* queue.
*/
if (!buffer) {
if (availableBuffers_.empty()) {
LOG(RPISTREAM, Warning) << "No buffers available for "
<< name_;
return -EINVAL;
}
buffer = availableBuffers_.front();
availableBuffers_.pop();
}
LOG(RPISTREAM, Debug) << "Queuing buffer " << buffer->cookie()
<< " for " << name_;
int ret = dev_->queueBuffer(buffer);
if (ret) {
LOG(RPISTREAM, Error) << "Failed to queue buffer for "
<< name_;
}
return ret;
}
int RPiStream::queueBuffers()
void RPiStream::returnBuffer(FrameBuffer *buffer)
{
/* This can only be called for external streams. */
assert(external_);
availableBuffers_.push(buffer);
}
int RPiStream::queueAllBuffers()
{
int ret;
if (external_)
return 0;
for (auto &b : internalBuffers_) {
int ret = dev_->queueBuffer(b.get());
if (ret) {
LOG(RPISTREAM, Error) << "Failed to queue buffers for "
<< name_;
while (!availableBuffers_.empty()) {
ret = queueBuffer(availableBuffers_.front());
if (ret < 0)
return ret;
}
availableBuffers_.pop();
}
return 0;
@@ -107,8 +149,14 @@ int RPiStream::queueBuffers()
void RPiStream::releaseBuffers()
{
dev_->releaseBuffers();
if (!external_ && !importOnly_)
internalBuffers_.clear();
clearBuffers();
}
void RPiStream::clearBuffers()
{
availableBuffers_ = std::queue<FrameBuffer *>{};
internalBuffers_.clear();
bufferList_.clear();
}
} /* namespace RPi */