libcamera: v4l2_videodevice: Add helper to queue all buffers

When starting the stream on a capture video device it is often needed to
queue all the allocated buffers. Add a helper method to do so, and
refactor the existing queueBuffer() method to make it clearer.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
This commit is contained in:
Laurent Pinchart 2019-07-09 13:56:55 +03:00
parent 33d3c4e204
commit 9bb36ec274
5 changed files with 70 additions and 24 deletions

View file

@ -139,6 +139,7 @@ public:
int releaseBuffers(); int releaseBuffers();
int queueBuffer(Buffer *buffer); int queueBuffer(Buffer *buffer);
std::vector<std::unique_ptr<Buffer>> queueAllBuffers();
Signal<Buffer *> bufferReady; Signal<Buffer *> bufferReady;
int streamOn(); int streamOn();

View file

@ -131,6 +131,7 @@ public:
CameraSensor *sensor_; CameraSensor *sensor_;
BufferPool pool_; BufferPool pool_;
std::vector<std::unique_ptr<Buffer>> buffers_;
}; };
class IPU3Stream : public Stream class IPU3Stream : public Stream
@ -1430,11 +1431,9 @@ int CIO2Device::start()
{ {
int ret; int ret;
for (Buffer &buffer : pool_.buffers()) { buffers_ = output_->queueAllBuffers();
ret = output_->queueBuffer(&buffer); if (buffers_.empty())
if (ret) return -EINVAL;
return ret;
}
ret = output_->streamOn(); ret = output_->streamOn();
if (ret) if (ret)
@ -1445,7 +1444,9 @@ int CIO2Device::start()
int CIO2Device::stop() int CIO2Device::stop()
{ {
return output_->streamOff(); int ret = output_->streamOff();
buffers_.clear();
return ret;
} }
int CIO2Device::mediaBusToFormat(unsigned int code) int CIO2Device::mediaBusToFormat(unsigned int code)

View file

@ -894,8 +894,8 @@ int V4L2VideoDevice::releaseBuffers()
*/ */
int V4L2VideoDevice::queueBuffer(Buffer *buffer) int V4L2VideoDevice::queueBuffer(Buffer *buffer)
{ {
struct v4l2_plane v4l2Planes[VIDEO_MAX_PLANES] = {};
struct v4l2_buffer buf = {}; struct v4l2_buffer buf = {};
struct v4l2_plane planes[VIDEO_MAX_PLANES] = {};
int ret; int ret;
buf.index = buffer->index(); buf.index = buffer->index();
@ -904,21 +904,20 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer)
buf.field = V4L2_FIELD_NONE; buf.field = V4L2_FIELD_NONE;
bool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type); bool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type);
const std::vector<Plane> &planes = buffer->planes();
if (buf.memory == V4L2_MEMORY_DMABUF) { if (buf.memory == V4L2_MEMORY_DMABUF) {
if (multiPlanar) { if (multiPlanar) {
for (unsigned int p = 0; for (unsigned int p = 0; p < planes.size(); ++p)
p < buffer->planes().size(); v4l2Planes[p].m.fd = planes[p].dmabuf();
p++)
planes[p].m.fd = buffer->planes()[p].dmabuf();
} else { } else {
buf.m.fd = buffer->planes()[0].dmabuf(); buf.m.fd = planes[0].dmabuf();
} }
} }
if (multiPlanar) { if (multiPlanar) {
buf.length = buffer->planes().size(); buf.length = planes.size();
buf.m.planes = planes; buf.m.planes = v4l2Planes;
} }
if (V4L2_TYPE_IS_OUTPUT(bufferType_)) { if (V4L2_TYPE_IS_OUTPUT(bufferType_)) {
@ -944,6 +943,53 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer)
return 0; return 0;
} }
/**
* \brief Queue all buffers into the video device
*
* When starting video capture users of the video device often need to queue
* all allocated buffers to the device. This helper method simplifies the
* implementation of the user by queuing all buffers and returning a vector of
* Buffer instances for each queued buffer.
*
* This method is meant to be used with video capture devices internal to a
* pipeline handler, such as ISP statistics capture devices, or raw CSI-2
* receivers. For video capture devices facing applications, buffers shall
* instead be queued when requests are received, and for video output devices,
* buffers shall be queued when frames are ready to be output.
*
* The caller shall ensure that the returned buffers vector remains valid until
* all the queued buffers are dequeued, either during capture, or by stopping
* the video device.
*
* Calling this method on an output device or on a device that has buffers
* already queued is an error and will return an empty vector.
*
* \return A vector of queued buffers, which will be empty if an error occurs
*/
std::vector<std::unique_ptr<Buffer>> V4L2VideoDevice::queueAllBuffers()
{
int ret;
if (queuedBuffersCount_)
return {};
if (V4L2_TYPE_IS_OUTPUT(bufferType_))
return {};
std::vector<std::unique_ptr<Buffer>> buffers;
for (unsigned int i = 0; i < bufferPool_->count(); ++i) {
Buffer *buffer = new Buffer();
buffer->index_ = i;
buffers.emplace_back(buffer);
ret = queueBuffer(buffer);
if (ret)
return {};
}
return buffers;
}
/** /**
* \brief Dequeue the next available buffer from the video device * \brief Dequeue the next available buffer from the video device
* *

View file

@ -117,11 +117,10 @@ protected:
capture_->bufferReady.connect(this, &BufferSharingTest::captureBufferReady); capture_->bufferReady.connect(this, &BufferSharingTest::captureBufferReady);
output_->bufferReady.connect(this, &BufferSharingTest::outputBufferReady); output_->bufferReady.connect(this, &BufferSharingTest::outputBufferReady);
/* Queue all the buffers to the capture device. */ std::vector<std::unique_ptr<Buffer>> buffers;
for (Buffer &buffer : pool_.buffers()) { buffers = capture_->queueAllBuffers();
if (capture_->queueBuffer(&buffer)) if (buffers.empty())
return TestFail; return TestFail;
}
ret = capture_->streamOn(); ret = capture_->streamOn();
if (ret) { if (ret) {

View file

@ -46,11 +46,10 @@ protected:
capture_->bufferReady.connect(this, &CaptureAsyncTest::receiveBuffer); capture_->bufferReady.connect(this, &CaptureAsyncTest::receiveBuffer);
/* Queue all the buffers to the device. */ std::vector<std::unique_ptr<Buffer>> buffers;
for (Buffer &b : pool_.buffers()) { buffers = capture_->queueAllBuffers();
if (capture_->queueBuffer(&b)) if (buffers.empty())
return TestFail; return TestFail;
}
ret = capture_->streamOn(); ret = capture_->streamOn();
if (ret) if (ret)