[libcamera-devel] [PATCH 6/9] libcamera: stream: Add operation to map buffers
Jacopo Mondi
jacopo at jmondi.org
Fri Jul 5 00:53:31 CEST 2019
Add and operation to map external buffers provided by applications in
a Request to the Stream's internal buffers used by the pipeline handlers
to interact with the video device.
Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
---
include/libcamera/buffer.h | 1 +
include/libcamera/stream.h | 6 ++
src/libcamera/stream.cpp | 116 +++++++++++++++++++++++++++++++++++++
3 files changed, 123 insertions(+)
diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
index 260a62e9e77e..d5d3dc90a096 100644
--- a/include/libcamera/buffer.h
+++ b/include/libcamera/buffer.h
@@ -59,6 +59,7 @@ private:
friend class BufferPool;
friend class PipelineHandler;
friend class Request;
+ friend class Stream;
friend class V4L2VideoDevice;
void cancel();
diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h
index 796f1aff2602..74415062cbdd 100644
--- a/include/libcamera/stream.h
+++ b/include/libcamera/stream.h
@@ -85,6 +85,8 @@ public:
const StreamConfiguration &configuration() const { return configuration_; }
MemoryType memoryType() const { return memoryType_; }
+ Buffer *mapBuffer(Buffer *applicationBuffer);
+
protected:
friend class Camera;
@@ -94,6 +96,10 @@ protected:
BufferPool bufferPool_;
StreamConfiguration configuration_;
MemoryType memoryType_;
+
+private:
+ std::vector<Buffer *> mappableBuffers_;
+ std::map<unsigned int, Buffer *> bufferMap_;
};
} /* namespace libcamera */
diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp
index 97e0f429c9fb..4585c0db77a4 100644
--- a/src/libcamera/stream.cpp
+++ b/src/libcamera/stream.cpp
@@ -13,6 +13,8 @@
#include <iomanip>
#include <sstream>
+#include <libcamera/request.h>
+
#include "log.h"
/**
@@ -470,6 +472,26 @@ void Stream::createBuffers(unsigned int count)
return;
bufferPool_.createBuffers(count);
+
+ /* Streams with internal memory usage do not need buffer mapping. */
+ if (memoryType_ == InternalMemory)
+ return;
+
+ /*
+ * Prepare for buffer mapping by queuing all buffers from the internal
+ * pool. Each external buffer presented by application will be mapped
+ * on an internal one.
+ */
+ mappableBuffers_.clear();
+ for (Buffer &buffer : bufferPool_.buffers()) {
+ /*
+ * Reserve all planes to support mapping multiplanar buffers.
+ * \todo I would use VIDEO_MAX_PLANES but it's a V4L2 thing.
+ */
+ buffer.planes().resize(3);
+
+ mappableBuffers_.push_back(&buffer);
+ }
}
/**
@@ -480,6 +502,100 @@ void Stream::destroyBuffers()
bufferPool_.destroyBuffers();
}
+/**
+ * \brief Map an application provided buffer to a stream buffer
+ * \param applicationBuffer The application provided buffer
+ *
+ * If a Stream has been configured to use application provided buffers a
+ * mapping between the external buffers and the internal ones, which are
+ * actually used to interface with the video device, is required.
+ *
+ * The most commonly used mechanism to perform zero-copy memory sharing
+ * on Linux-based system is dmabuf, which allows user-space applications to
+ * share buffers by exchanging dmabuf generated file descriptors. This
+ * operations assumes that all application provided buffers have each of their
+ * used memory planes exported as dmabuf file descriptor, to copy them in
+ * the buffer to be then queued on the video device by pipeline handlers.
+ *
+ * Perform mapping by maintaining a cache in a map associating the dmabuf file
+ * descriptor of the application provided buffer to one of the stream's internal
+ * buffers to provide pipeline handlers the buffer to use to interact with video
+ * devices.
+ *
+ * Once the buffer completes, the mapping should be reverted to return to
+ * the application the buffer it first provided here.
+ *
+ * \return The stream buffer which maps to the application provided buffer
+ */
+Buffer *Stream::mapBuffer(Buffer *applicationBuffer)
+{
+ unsigned int key = applicationBuffer->planes()[0].dmabuf();
+
+ /*
+ * Buffer hit: the application buffer has already been mapped, return
+ * the assigned stream buffer
+ */
+ auto mapped = bufferMap_.find(key);
+ if (mapped != bufferMap_.end()) {
+ Buffer *buffer = mapped->second;
+
+ /*
+ * Keep the mappableBuffers_ vector warm: move the hit buffer to
+ * the vector end as on a buffer miss buffers are below evicted
+ * from the vector head.
+ */
+ auto it = std::find(mappableBuffers_.begin(),
+ mappableBuffers_.end(), buffer);
+
+ ASSERT(it != mappableBuffers_.end());
+ std::rotate(it, it + 1, mappableBuffers_.end());
+
+ return buffer;
+ }
+
+ /*
+ * Buffer miss: assign to the application buffer the stream buffer
+ * at mappableBuffers_ begin, then move it to the end.
+ */
+ Buffer *buffer = *(mappableBuffers_.begin());
+ std::rotate(mappableBuffers_.begin(), mappableBuffers_.begin() + 1,
+ mappableBuffers_.end());
+
+
+ /* Remove the [key, buffer] entry buffer from the buffer map */
+ auto deadBuf = std::find_if(bufferMap_.begin(), bufferMap_.end(),
+ [&](std::pair<const unsigned int, Buffer *> &map) {
+ return bufferMap_[map.first] == buffer;
+ });
+ if (deadBuf != bufferMap_.end())
+ bufferMap_.erase(deadBuf);
+
+ /*
+ * Assign the buffer by copying the dmabuf file descriptors from the
+ * application provided buffer.
+ */
+ for (unsigned int i = 0; i < applicationBuffer->planes().size(); ++i) {
+ int fd = applicationBuffer->planes()[i].dmabuf();
+
+ /*
+ * The ARC camera stack seems to report more planes that the
+ * ones it actually uses.
+ */
+ if (fd < 0)
+ break;
+
+ buffer->planes()[i].setDmabuf(fd, 0);
+ }
+
+ /* Pipeline handlers use request_ at buffer completion time. */
+ buffer->request_ = applicationBuffer->request();
+
+ /* And finally, store the mapping for later re-use and return it. */
+ bufferMap_[key] = buffer;
+
+ return buffer;
+}
+
/**
* \var Stream::bufferPool_
* \brief The pool of buffers associated with the stream
--
2.21.0
More information about the libcamera-devel
mailing list