[libcamera-devel] [RFC 08/12] libcamera: buffer: Add a buffer allocator

Laurent Pinchart laurent.pinchart at ideasonboard.com
Mon Nov 18 20:49:24 CET 2019


Hi Niklas,

Thank you for the patch.

On Fri, Nov 15, 2019 at 04:11:56PM +0100, Jacopo Mondi wrote:
> On Mon, Oct 28, 2019 at 03:25:21AM +0100, Niklas Söderlund wrote:
> > From an applications point of view buffers shall not be allocated
> > directly by a camera or stream. Instead they shall be allocated
> > externally and used by a camera.
> >
> > To model this behavior add a buffer allocator that is exposed to
> > applications. How the allocator creates buffers is pipeline specific and

s/pipeline specific/pipeline-specific/

> > handled by sub-classing the stream class.
> 
> I would dare to question the whole allocator idea here... I mean, the
> allocator basically uses the streams' virtual allocateBuffer, whose
> importBuffers counterpart is the invoked at Camera::start().

I like the allocator, but I would dare to question the commit message
:-) I know what this is about as we've discussed it during the last code
camp, but it's not very clear here. Documentation will help, and I think
you could then rewrite the commit message with some excerpts of the
documentation. It should explain the design idea behind this (which is,
in a nutshell, that the libcamera API should be designed based on
externally-allocated buffers, with a helper allocator to provide buffers
to applications that can't allocate them externally).

> We still need to call Camera::allocateBuffers() for internal buffers
> allocation, which makes the 'buffer allocation' operations quite a
> few...
> 
> I wonder if, as the allocator really need to be exposed to
> applications..
> 
> I assume for the internally allocated buffer it could be simply
> replaced by calling Stream::allocate() on each Stream part of the
> camera configuration.
> 
> I still fail to find how externally allocated buffers are imported
> now, will they go through the buffer allocator as well ?
> 
> > Signed-off-by: Niklas Söderlund <niklas.soderlund at ragnatech.se>
> > ---
> >  include/libcamera/buffer.h | 17 ++++++++++++++
> >  include/libcamera/stream.h |  1 +
> >  src/libcamera/buffer.cpp   | 48 ++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 66 insertions(+)
> >
> > diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
> > index c626f669040b3c04..adb642ad5da072d2 100644
> > --- a/include/libcamera/buffer.h
> > +++ b/include/libcamera/buffer.h
> > @@ -8,11 +8,14 @@
> >  #define __LIBCAMERA_BUFFER_H__
> >
> >  #include <array>
> > +#include <map>
> > +#include <memory>
> >  #include <stdint.h>
> >  #include <vector>
> >
> >  namespace libcamera {
> >
> > +class Camera;
> >  class Request;
> >  class Stream;
> >
> > @@ -139,6 +142,20 @@ private:
> >  	std::vector<PlaneInfo> planes_;
> >  };
> >
> > +class BufferAllocator
> > +{
> > +public:
> > +	BufferAllocator(std::shared_ptr<Camera> camera);
> > +	~BufferAllocator();
> > +
> > +	int allocate(Stream *stream);

I like the buffer allocator concept, but I wonder if the API is right.
You rely on allocate() being called after Camera::configure() to let the
pipeline handler determine the buffers sizes. Isn't this a bit too
limiting ? Shouldn't we let the application request a specific size
instead ? This would require usage of VIDIOC_CREATE_BUFS in
V4L2VideoDevice. Do you think it's overkill, and applications that need
to allocate buffers with larger sizes should then allocate them
externally ?

The lack of free() makes the API unbalanced. It feels a bit fragile, but
I'd have to see how this class is used to judge if that's fine.

> > +	std::vector<Buffer *> get(Stream *stream);

Should this return a const std::vector<Buffer *> & ? Or do you envision
the caller would need a writable copy anyway ?

> > +
> > +private:
> > +	std::shared_ptr<Camera> camera_;
> > +	std::map<Stream *, std::vector<Buffer *>> allocated_;

s/allocated_/buffers_/ ?

> > +};
> > +

This should be moved to a separate file to show that it's really an
optional helper and not part of the core buffer API.

> >  } /* namespace libcamera */
> >
> >  #endif /* __LIBCAMERA_BUFFER_H__ */
> > diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h
> > index dac4831cfa1a9b1d..b051341511a7ab7c 100644
> > --- a/include/libcamera/stream.h
> > +++ b/include/libcamera/stream.h
> > @@ -84,6 +84,7 @@ public:
> >  	MemoryType memoryType() const { return memoryType_; }
> >
> >  protected:
> > +	friend class BufferAllocator;

Does this belong to a separate patch ?

> >  	friend class Camera;
> >
> >  	virtual int allocateBuffers(std::vector<Buffer *> *buffers) { return -EINVAL; }
> > diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
> > index 10b16a862393b536..fce1ce5e49cbbf42 100644
> > --- a/src/libcamera/buffer.cpp
> > +++ b/src/libcamera/buffer.cpp
> > @@ -12,6 +12,9 @@
> >  #include <sys/mman.h>
> >  #include <unistd.h>
> >
> > +#include <libcamera/camera.h>
> > +#include <libcamera/stream.h>
> > +
> >  #include "log.h"
> >
> >  /**
> > @@ -393,4 +396,49 @@ void Buffer::cancel()
> >   * The intended callers are Request::prepare() and Request::completeBuffer().
> >   */
> >
> > +BufferAllocator::BufferAllocator(std::shared_ptr<Camera> camera)
> > +	: camera_(camera)
> > +{
> > +}
> > +
> > +BufferAllocator::~BufferAllocator()
> > +{
> > +	for (auto &it : allocated_) {

It just occurred to me that, technically speaking, this isn't an
iterator, so the name "it" isn't the best. The variable's type is
decltype(allocated_)::value_type, which resolves to

std::pair<const Stream *, std::vector<Buffer *>>

Maybe this should be renamed to streamBuffers ? Or possibly value ? Or
another short name we could use through the libcamera code base in
similar cases (val ? mapval ?) ? Or maybe this is overkill :-)

> > +		Stream *stream = it.first;
> > +		std::vector<Buffer *> &buffers = it.second;
> > +
> > +		for (Buffer *buffer : buffers)
> > +			delete buffer;
> > +
> > +		buffers.clear();
> > +
> > +		stream->allocateBuffers(nullptr);

Wouldn't Stream::freeBuffers() be a nicer name ?

> > +	}
> > +}
> > +
> > +int BufferAllocator::allocate(Stream *stream)
> > +{
> > +	bool found = false;
> > +
> > +	for (const Stream *s : camera_->streams()) {
> > +		if (stream == s) {
> > +			found = true;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (!found)
> > +		return -EINVAL;

Should we log a message here ? Should we store a pointer to the camera
in the Stream class to facilitate this kind of error checking ?

> > +
> > +	return stream->allocateBuffers(&allocated_[stream]);
> > +}
> > +
> > +std::vector<Buffer *> BufferAllocator::get(Stream *stream)
> > +{
> > +	if (allocated_.find(stream) == allocated_.end())
> > +		return {};
> > +
> > +	return allocated_[stream];

This results in a double lookup.

	auto iter = allocated_.find(stream);
	if (iter == allocated_.end())
		return {};

	return iter->second;

> > +}
> > +
> >  } /* namespace libcamera */

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list