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

@ -186,7 +186,8 @@ private:
void checkRequestCompleted(); void checkRequestCompleted();
void tryRunPipeline(); void tryRunPipeline();
void tryFlushQueues(); void tryFlushQueues();
FrameBuffer *updateQueue(std::queue<FrameBuffer *> &q, uint64_t timestamp, V4L2VideoDevice *dev); FrameBuffer *updateQueue(std::queue<FrameBuffer *> &q, uint64_t timestamp,
RPi::RPiStream *stream);
unsigned int ispOutputCount_; unsigned int ispOutputCount_;
}; };
@ -507,8 +508,15 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
StreamConfiguration &cfg = config->at(i); StreamConfiguration &cfg = config->at(i);
if (isRaw(cfg.pixelFormat)) { if (isRaw(cfg.pixelFormat)) {
cfg.setStream(&data->isp_[Isp::Input]); cfg.setStream(&data->unicam_[Unicam::Image]);
data->isp_[Isp::Input].setExternal(true); /*
* We must set both Unicam streams as external, even
* though the application may only request RAW frames.
* This is because we match timestamps on both streams
* to synchronise buffers.
*/
data->unicam_[Unicam::Image].setExternal(true);
data->unicam_[Unicam::Embedded].setExternal(true);
continue; continue;
} }
@ -611,7 +619,7 @@ int PipelineHandlerRPi::exportFrameBuffers([[maybe_unused]] Camera *camera, Stre
unsigned int count = stream->configuration().bufferCount; unsigned int count = stream->configuration().bufferCount;
int ret = s->dev()->exportBuffers(count, buffers); int ret = s->dev()->exportBuffers(count, buffers);
s->setExternalBuffers(buffers); s->setExportedBuffers(buffers);
return ret; return ret;
} }
@ -625,6 +633,7 @@ int PipelineHandlerRPi::start(Camera *camera)
ret = prepareBuffers(camera); ret = prepareBuffers(camera);
if (ret) { if (ret) {
LOG(RPI, Error) << "Failed to allocate buffers"; LOG(RPI, Error) << "Failed to allocate buffers";
stop(camera);
return ret; return ret;
} }
@ -710,14 +719,26 @@ int PipelineHandlerRPi::queueRequestDevice(Camera *camera, Request *request)
if (data->state_ == RPiCameraData::State::Stopped) if (data->state_ == RPiCameraData::State::Stopped)
return -EINVAL; return -EINVAL;
/* Ensure all external streams have associated buffers! */ LOG(RPI, Debug) << "queueRequestDevice: New request.";
for (auto &stream : data->isp_) {
if (!stream.isExternal())
continue;
if (!request->findBuffer(&stream)) { /* Push all buffers supplied in the Request to the respective streams. */
LOG(RPI, Error) << "Attempt to queue request with invalid stream."; for (auto stream : data->streams_) {
return -ENOENT; if (stream->isExternal()) {
FrameBuffer *buffer = request->findBuffer(stream);
/*
* If no buffer is provided by the request for this stream, we
* queue a nullptr to the stream to signify that it must use an
* internally allocated buffer for this capture request. This
* buffer will not be given back to the application, but is used
* to support the internal pipeline flow.
*
* The below queueBuffer() call will do nothing if there are not
* enough internal buffers allocated, but this will be handled by
* queuing the request for buffers in the RPiStream object.
*/
int ret = stream->queueBuffer(buffer);
if (ret)
return ret;
} }
} }
@ -816,14 +837,14 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)
data->unicam_[Unicam::Image].dev()->setControls(&ctrls); data->unicam_[Unicam::Image].dev()->setControls(&ctrls);
/* /*
* List the available output streams. * List the available streams an application may request. At present, we
* Currently cannot do Unicam streams! * do not advertise Unicam Embedded and ISP Statistics streams, as there
* is no mechanism for the application to request non-image buffer formats.
*/ */
std::set<Stream *> streams; std::set<Stream *> streams;
streams.insert(&data->isp_[Isp::Input]); streams.insert(&data->unicam_[Unicam::Image]);
streams.insert(&data->isp_[Isp::Output0]); streams.insert(&data->isp_[Isp::Output0]);
streams.insert(&data->isp_[Isp::Output1]); streams.insert(&data->isp_[Isp::Output1]);
streams.insert(&data->isp_[Isp::Stats]);
/* Create and register the camera. */ /* Create and register the camera. */
std::shared_ptr<Camera> camera = std::shared_ptr<Camera> camera =
@ -839,9 +860,28 @@ int PipelineHandlerRPi::queueAllBuffers(Camera *camera)
int ret; int ret;
for (auto const stream : data->streams_) { for (auto const stream : data->streams_) {
ret = stream->queueBuffers(); if (!stream->isExternal()) {
ret = stream->queueAllBuffers();
if (ret < 0) if (ret < 0)
return ret; return ret;
} else {
/*
* For external streams, we must queue up a set of internal
* buffers to handle the number of drop frames requested by
* the IPA. This is done by passing nullptr in queueBuffer().
*
* The below queueBuffer() call will do nothing if there
* are not enough internal buffers allocated, but this will
* be handled by queuing the request for buffers in the
* RPiStream object.
*/
unsigned int i;
for (i = 0; i < data->dropFrameCount_; i++) {
int ret = stream->queueBuffer(nullptr);
if (ret)
return ret;
}
}
} }
return 0; return 0;
@ -855,7 +895,8 @@ int PipelineHandlerRPi::prepareBuffers(Camera *camera)
/* /*
* Decide how many internal buffers to allocate. For now, simply look * Decide how many internal buffers to allocate. For now, simply look
* at how many external buffers will be provided. Will need to improve * at how many external buffers will be provided. Will need to improve
* this logic. * this logic. However, we really must have all streams allocate the same
* number of buffers to simplify error handling in queueRequestDevice().
*/ */
unsigned int maxBuffers = 0; unsigned int maxBuffers = 0;
for (const Stream *s : camera->streams()) for (const Stream *s : camera->streams())
@ -863,33 +904,9 @@ int PipelineHandlerRPi::prepareBuffers(Camera *camera)
maxBuffers = std::max(maxBuffers, s->configuration().bufferCount); maxBuffers = std::max(maxBuffers, s->configuration().bufferCount);
for (auto const stream : data->streams_) { for (auto const stream : data->streams_) {
if (stream->isExternal() || stream->isImporter()) { ret = stream->prepareBuffers(maxBuffers);
/*
* If a stream is marked as external reserve memory to
* prepare to import as many buffers are requested in
* the stream configuration.
*
* If a stream is an internal stream with importer
* role, reserve as many buffers as possible.
*/
unsigned int count = stream->isExternal()
? stream->configuration().bufferCount
: maxBuffers;
ret = stream->importBuffers(count);
if (ret < 0) if (ret < 0)
return ret; return ret;
} else {
/*
* If the stream is an internal exporter allocate and
* export as many buffers as possible to its internal
* pool.
*/
ret = stream->allocateBuffers(maxBuffers);
if (ret < 0) {
freeBuffers(camera);
return ret;
}
}
} }
/* /*
@ -897,7 +914,7 @@ int PipelineHandlerRPi::prepareBuffers(Camera *camera)
* the IPA and RPI_IPA_EVENT_SIGNAL_ISP_PREPARE event. * the IPA and RPI_IPA_EVENT_SIGNAL_ISP_PREPARE event.
*/ */
count = 0; count = 0;
for (auto const &b : *data->unicam_[Unicam::Image].getBuffers()) { for (auto const &b : data->unicam_[Unicam::Image].getBuffers()) {
b->setCookie(count++); b->setCookie(count++);
} }
@ -906,14 +923,14 @@ int PipelineHandlerRPi::prepareBuffers(Camera *camera)
* the IPA. * the IPA.
*/ */
count = 0; count = 0;
for (auto const &b : *data->isp_[Isp::Stats].getBuffers()) { for (auto const &b : data->isp_[Isp::Stats].getBuffers()) {
b->setCookie(count++); b->setCookie(count++);
data->ipaBuffers_.push_back({ .id = RPiIpaMask::STATS | b->cookie(), data->ipaBuffers_.push_back({ .id = RPiIpaMask::STATS | b->cookie(),
.planes = b->planes() }); .planes = b->planes() });
} }
count = 0; count = 0;
for (auto const &b : *data->unicam_[Unicam::Embedded].getBuffers()) { for (auto const &b : data->unicam_[Unicam::Embedded].getBuffers()) {
b->setCookie(count++); b->setCookie(count++);
data->ipaBuffers_.push_back({ .id = RPiIpaMask::EMBEDDED_DATA | b->cookie(), data->ipaBuffers_.push_back({ .id = RPiIpaMask::EMBEDDED_DATA | b->cookie(),
.planes = b->planes() }); .planes = b->planes() });
@ -1068,7 +1085,7 @@ void RPiCameraData::queueFrameAction([[maybe_unused]] unsigned int frame,
switch (action.operation) { switch (action.operation) {
case RPI_IPA_ACTION_STATS_METADATA_COMPLETE: { case RPI_IPA_ACTION_STATS_METADATA_COMPLETE: {
unsigned int bufferId = action.data[0]; unsigned int bufferId = action.data[0];
FrameBuffer *buffer = isp_[Isp::Stats].getBuffers()->at(bufferId).get(); FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(bufferId);
handleStreamBuffer(buffer, &isp_[Isp::Stats]); handleStreamBuffer(buffer, &isp_[Isp::Stats]);
/* Fill the Request metadata buffer with what the IPA has provided */ /* Fill the Request metadata buffer with what the IPA has provided */
@ -1079,19 +1096,19 @@ void RPiCameraData::queueFrameAction([[maybe_unused]] unsigned int frame,
case RPI_IPA_ACTION_EMBEDDED_COMPLETE: { case RPI_IPA_ACTION_EMBEDDED_COMPLETE: {
unsigned int bufferId = action.data[0]; unsigned int bufferId = action.data[0];
FrameBuffer *buffer = unicam_[Unicam::Embedded].getBuffers()->at(bufferId).get(); FrameBuffer *buffer = unicam_[Unicam::Embedded].getBuffers().at(bufferId);
handleStreamBuffer(buffer, &unicam_[Unicam::Embedded]); handleStreamBuffer(buffer, &unicam_[Unicam::Embedded]);
break; break;
} }
case RPI_IPA_ACTION_RUN_ISP: { case RPI_IPA_ACTION_RUN_ISP: {
unsigned int bufferId = action.data[0]; unsigned int bufferId = action.data[0];
FrameBuffer *buffer = unicam_[Unicam::Image].getBuffers()->at(bufferId).get(); FrameBuffer *buffer = unicam_[Unicam::Image].getBuffers().at(bufferId);
LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << buffer->cookie() LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << buffer->cookie()
<< ", timestamp: " << buffer->metadata().timestamp; << ", timestamp: " << buffer->metadata().timestamp;
isp_[Isp::Input].dev()->queueBuffer(buffer); isp_[Isp::Input].queueBuffer(buffer);
ispOutputCount_ = 0; ispOutputCount_ = 0;
break; break;
} }
@ -1192,7 +1209,19 @@ void RPiCameraData::ispOutputDequeue(FrameBuffer *buffer)
<< ", buffer id " << buffer->cookie() << ", buffer id " << buffer->cookie()
<< ", timestamp: " << buffer->metadata().timestamp; << ", timestamp: " << buffer->metadata().timestamp;
/*
* ISP statistics buffer must not be re-queued or sent back to the
* application until after the IPA signals so.
*/
if (stream == &isp_[Isp::Stats]) {
IPAOperationData op;
op.operation = RPI_IPA_EVENT_SIGNAL_STAT_READY;
op.data = { RPiIpaMask::STATS | buffer->cookie() };
ipa_->processEvent(op);
} else {
/* Any other ISP output can be handed back to the application now. */
handleStreamBuffer(buffer, stream); handleStreamBuffer(buffer, stream);
}
/* /*
* Increment the number of ISP outputs generated. * Increment the number of ISP outputs generated.
@ -1200,14 +1229,6 @@ void RPiCameraData::ispOutputDequeue(FrameBuffer *buffer)
*/ */
ispOutputCount_++; ispOutputCount_++;
/* If this is a stats output, hand it to the IPA now. */
if (stream == &isp_[Isp::Stats]) {
IPAOperationData op;
op.operation = RPI_IPA_EVENT_SIGNAL_STAT_READY;
op.data = { RPiIpaMask::STATS | buffer->cookie() };
ipa_->processEvent(op);
}
handleState(); handleState();
} }
@ -1220,8 +1241,12 @@ void RPiCameraData::clearIncompleteRequests()
*/ */
for (auto const request : requestQueue_) { for (auto const request : requestQueue_) {
for (auto const stream : streams_) { for (auto const stream : streams_) {
if (stream->isExternal()) if (!stream->isExternal())
stream->dev()->queueBuffer(request->findBuffer(stream)); continue;
FrameBuffer *buffer = request->findBuffer(stream);
if (buffer)
stream->queueBuffer(buffer);
} }
} }
@ -1250,7 +1275,7 @@ void RPiCameraData::clearIncompleteRequests()
* Has the buffer already been handed back to the * Has the buffer already been handed back to the
* request? If not, do so now. * request? If not, do so now.
*/ */
if (buffer->request()) if (buffer && buffer->request())
pipe_->completeBuffer(camera_, request, buffer); pipe_->completeBuffer(camera_, request, buffer);
} }
@ -1262,30 +1287,28 @@ void RPiCameraData::clearIncompleteRequests()
void RPiCameraData::handleStreamBuffer(FrameBuffer *buffer, RPi::RPiStream *stream) void RPiCameraData::handleStreamBuffer(FrameBuffer *buffer, RPi::RPiStream *stream)
{ {
if (stream->isExternal()) { if (stream->isExternal()) {
if (!dropFrameCount_) { /*
Request *request = buffer->request(); * It is possible to be here without a pending request, so check
* that we actually have one to action, otherwise we just return
* buffer back to the stream.
*/
Request *request = requestQueue_.empty() ? nullptr : requestQueue_.front();
if (!dropFrameCount_ && request && request->findBuffer(stream) == buffer) {
/*
* Tag the buffer as completed, returning it to the
* application.
*/
pipe_->completeBuffer(camera_, request, buffer); pipe_->completeBuffer(camera_, request, buffer);
} else {
/*
* This buffer was not part of the Request, or there is no
* pending request, so we can recycle it.
*/
stream->returnBuffer(buffer);
} }
} else { } else {
/* Special handling for RAW buffer Requests. /* Simply re-queue the buffer to the requested stream. */
* stream->queueBuffer(buffer);
* The ISP input stream is alway an import stream, but if the
* current Request has been made for a buffer on the stream,
* simply memcpy to the Request buffer and requeue back to the
* device.
*/
if (stream == &unicam_[Unicam::Image] && !dropFrameCount_) {
const Stream *rawStream = static_cast<const Stream *>(&isp_[Isp::Input]);
Request *request = requestQueue_.front();
FrameBuffer *raw = request->findBuffer(const_cast<Stream *>(rawStream));
if (raw) {
raw->copyFrom(buffer);
pipe_->completeBuffer(camera_, request, raw);
}
}
/* Simply requeue the buffer. */
stream->dev()->queueBuffer(buffer);
} }
} }
@ -1367,7 +1390,7 @@ void RPiCameraData::tryRunPipeline()
* current bayer buffer will be removed and re-queued to the driver. * current bayer buffer will be removed and re-queued to the driver.
*/ */
embeddedBuffer = updateQueue(embeddedQueue_, bayerBuffer->metadata().timestamp, embeddedBuffer = updateQueue(embeddedQueue_, bayerBuffer->metadata().timestamp,
unicam_[Unicam::Embedded].dev()); &unicam_[Unicam::Embedded]);
if (!embeddedBuffer) { if (!embeddedBuffer) {
LOG(RPI, Debug) << "Could not find matching embedded buffer"; LOG(RPI, Debug) << "Could not find matching embedded buffer";
@ -1386,7 +1409,7 @@ void RPiCameraData::tryRunPipeline()
embeddedBuffer = embeddedQueue_.front(); embeddedBuffer = embeddedQueue_.front();
bayerBuffer = updateQueue(bayerQueue_, embeddedBuffer->metadata().timestamp, bayerBuffer = updateQueue(bayerQueue_, embeddedBuffer->metadata().timestamp,
unicam_[Unicam::Image].dev()); &unicam_[Unicam::Image]);
if (!bayerBuffer) { if (!bayerBuffer) {
LOG(RPI, Debug) << "Could not find matching bayer buffer - ending."; LOG(RPI, Debug) << "Could not find matching bayer buffer - ending.";
@ -1394,11 +1417,7 @@ void RPiCameraData::tryRunPipeline()
} }
} }
/* /* Take the first request from the queue and action the IPA. */
* Take the first request from the queue and action the IPA.
* Unicam buffers for the request have already been queued as they come
* in.
*/
Request *request = requestQueue_.front(); Request *request = requestQueue_.front();
/* /*
@ -1410,12 +1429,6 @@ void RPiCameraData::tryRunPipeline()
op.controls = { request->controls() }; op.controls = { request->controls() };
ipa_->processEvent(op); ipa_->processEvent(op);
/* Queue up any ISP buffers passed into the request. */
for (auto &stream : isp_) {
if (stream.isExternal())
stream.dev()->queueBuffer(request->findBuffer(&stream));
}
/* Ready to use the buffers, pop them off the queue. */ /* Ready to use the buffers, pop them off the queue. */
bayerQueue_.pop(); bayerQueue_.pop();
embeddedQueue_.pop(); embeddedQueue_.pop();
@ -1445,32 +1458,42 @@ void RPiCameraData::tryFlushQueues()
* and give a chance for the hardware to return to lock-step. We do have * and give a chance for the hardware to return to lock-step. We do have
* to drop all interim frames. * to drop all interim frames.
*/ */
if (unicam_[Unicam::Image].getBuffers()->size() == bayerQueue_.size() && if (unicam_[Unicam::Image].getBuffers().size() == bayerQueue_.size() &&
unicam_[Unicam::Embedded].getBuffers()->size() == embeddedQueue_.size()) { unicam_[Unicam::Embedded].getBuffers().size() == embeddedQueue_.size()) {
/* This cannot happen when Unicam streams are external. */
assert(!unicam_[Unicam::Image].isExternal());
LOG(RPI, Warning) << "Flushing all buffer queues!"; LOG(RPI, Warning) << "Flushing all buffer queues!";
while (!bayerQueue_.empty()) { while (!bayerQueue_.empty()) {
unicam_[Unicam::Image].dev()->queueBuffer(bayerQueue_.front()); unicam_[Unicam::Image].queueBuffer(bayerQueue_.front());
bayerQueue_.pop(); bayerQueue_.pop();
} }
while (!embeddedQueue_.empty()) { while (!embeddedQueue_.empty()) {
unicam_[Unicam::Embedded].dev()->queueBuffer(embeddedQueue_.front()); unicam_[Unicam::Embedded].queueBuffer(embeddedQueue_.front());
embeddedQueue_.pop(); embeddedQueue_.pop();
} }
} }
} }
FrameBuffer *RPiCameraData::updateQueue(std::queue<FrameBuffer *> &q, uint64_t timestamp, FrameBuffer *RPiCameraData::updateQueue(std::queue<FrameBuffer *> &q, uint64_t timestamp,
V4L2VideoDevice *dev) RPi::RPiStream *stream)
{ {
/*
* If the unicam streams are external (both have be to the same), then we
* can only return out the top buffer in the queue, and assume they have
* been synced by queuing at the same time. We cannot drop these frames,
* as they may have been provided externally.
*/
while (!q.empty()) { while (!q.empty()) {
FrameBuffer *b = q.front(); FrameBuffer *b = q.front();
if (b->metadata().timestamp < timestamp) { if (!stream->isExternal() && b->metadata().timestamp < timestamp) {
q.pop(); q.pop();
dev->queueBuffer(b); stream->queueBuffer(b);
LOG(RPI, Warning) << "Dropping input frame!"; LOG(RPI, Warning) << "Dropping unmatched input frame in stream "
} else if (b->metadata().timestamp == timestamp) { << stream->name();
} else if (stream->isExternal() || b->metadata().timestamp == timestamp) {
/* The calling function will pop the item from the queue. */ /* The calling function will pop the item from the queue. */
return b; return b;
} else { } else {

View file

@ -27,78 +27,120 @@ std::string RPiStream::name() const
void RPiStream::reset() void RPiStream::reset()
{ {
external_ = false; external_ = false;
internalBuffers_.clear(); clearBuffers();
}
bool RPiStream::isImporter() const
{
return importOnly_;
} }
void RPiStream::setExternal(bool external) void RPiStream::setExternal(bool external)
{ {
/* Import streams cannot be external. */
ASSERT(!external || !importOnly_);
external_ = external; external_ = external;
} }
bool RPiStream::isExternal() const bool RPiStream::isExternal() const
{ {
/* return external_;
* 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_;
} }
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 bool RPiStream::findFrameBuffer(FrameBuffer *buffer) const
{ {
auto start = external_ ? externalBuffers_->begin() : internalBuffers_.begin();
auto end = external_ ? externalBuffers_->end() : internalBuffers_.end();
if (importOnly_) if (importOnly_)
return false; return false;
if (std::find_if(start, end, if (std::find(bufferList_.begin(), bufferList_.end(), buffer) != bufferList_.end())
[buffer](std::unique_ptr<FrameBuffer> const &ref) { return ref.get() == buffer; }) != end)
return true; return true;
return false; 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); 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_) if (external_)
return 0; return 0;
for (auto &b : internalBuffers_) { while (!availableBuffers_.empty()) {
int ret = dev_->queueBuffer(b.get()); ret = queueBuffer(availableBuffers_.front());
if (ret) { if (ret < 0)
LOG(RPISTREAM, Error) << "Failed to queue buffers for "
<< name_;
return ret; return ret;
}
availableBuffers_.pop();
} }
return 0; return 0;
@ -107,8 +149,14 @@ int RPiStream::queueBuffers()
void RPiStream::releaseBuffers() void RPiStream::releaseBuffers()
{ {
dev_->releaseBuffers(); dev_->releaseBuffers();
if (!external_ && !importOnly_) clearBuffers();
}
void RPiStream::clearBuffers()
{
availableBuffers_ = std::queue<FrameBuffer *>{};
internalBuffers_.clear(); internalBuffers_.clear();
bufferList_.clear();
} }
} /* namespace RPi */ } /* namespace RPi */

View file

@ -44,20 +44,23 @@ public:
void setExternal(bool external); void setExternal(bool external);
bool isExternal() const; bool isExternal() const;
void setExternalBuffers(std::vector<std::unique_ptr<FrameBuffer>> *buffers); void setExportedBuffers(std::vector<std::unique_ptr<FrameBuffer>> *buffers);
const std::vector<std::unique_ptr<FrameBuffer>> *getBuffers() const; const std::vector<FrameBuffer *> &getBuffers() const;
bool findFrameBuffer(FrameBuffer *buffer) const; bool findFrameBuffer(FrameBuffer *buffer) const;
int importBuffers(unsigned int count); int prepareBuffers(unsigned int count);
int allocateBuffers(unsigned int count); int queueBuffer(FrameBuffer *buffer);
void returnBuffer(FrameBuffer *buffer);
int queueBuffers(); int queueAllBuffers();
void releaseBuffers(); void releaseBuffers();
private: private:
void clearBuffers();
/* /*
* Indicates that this stream is active externally, i.e. the buffers * Indicates that this stream is active externally, i.e. the buffers
* are provided by the application. * might be provided by (and returned to) the application.
*/ */
bool external_; bool external_;
@ -70,11 +73,21 @@ private:
/* The actual device stream. */ /* The actual device stream. */
std::unique_ptr<V4L2VideoDevice> dev_; std::unique_ptr<V4L2VideoDevice> dev_;
/* Internally allocated framebuffers associated with this device stream. */ /* All framebuffers associated with this device stream. */
std::vector<std::unique_ptr<FrameBuffer>> internalBuffers_; std::vector<FrameBuffer *> bufferList_;
/* Externally allocated framebuffers associated with this device stream. */ /*
std::vector<std::unique_ptr<FrameBuffer>> *externalBuffers_; * List of frame buffers that we can use if none have been provided by
* the application for external streams. This is populated by the
* buffers exported internally.
*/
std::queue<FrameBuffer *> availableBuffers_;
/*
* This is a list of buffers exported internally. Need to keep this around
* as the stream needs to maintain ownership of these buffers.
*/
std::vector<std::unique_ptr<FrameBuffer>> internalBuffers_;
}; };
/* /*