summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libcamera/buffer.h2
-rw-r--r--include/libcamera/stream.h6
-rw-r--r--src/libcamera/camera.cpp20
-rw-r--r--src/libcamera/stream.cpp96
4 files changed, 123 insertions, 1 deletions
diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
index fc5c7d4c..7b657509 100644
--- a/include/libcamera/buffer.h
+++ b/include/libcamera/buffer.h
@@ -30,6 +30,8 @@ public:
unsigned int length() const { return length_; }
private:
+ friend class Stream;
+
int mmap();
int munmap();
diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h
index 1883d9e9..2e619cdf 100644
--- a/include/libcamera/stream.h
+++ b/include/libcamera/stream.h
@@ -85,12 +85,18 @@ public:
protected:
friend class Camera;
+ int mapBuffer(const Buffer *buffer);
+ void unmapBuffer(const Buffer *buffer);
+
void createBuffers(MemoryType memory, unsigned int count);
void destroyBuffers();
BufferPool bufferPool_;
StreamConfiguration configuration_;
MemoryType memoryType_;
+
+private:
+ std::vector<std::pair<std::array<int, 3>, unsigned int>> bufferCache_;
};
} /* namespace libcamera */
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index db15fd46..f2deb38d 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -800,6 +800,7 @@ Request *Camera::createRequest(uint64_t cookie)
* \retval -ENODEV The camera has been disconnected from the system
* \retval -EACCES The camera is not running so requests can't be queued
* \retval -EINVAL The request is invalid
+ * \retval -ENOMEM No buffer memory was available to handle the request
*/
int Camera::queueRequest(Request *request)
{
@@ -818,6 +819,16 @@ int Camera::queueRequest(Request *request)
return -EINVAL;
}
+ if (stream->memoryType() == ExternalMemory) {
+ int index = stream->mapBuffer(buffer);
+ if (index < 0) {
+ LOG(Camera, Error) << "No buffer memory available";
+ return -ENOMEM;
+ }
+
+ buffer->index_ = index;
+ }
+
buffer->mem_ = &stream->buffers()[buffer->index_];
}
@@ -901,7 +912,14 @@ int Camera::stop()
*/
void Camera::requestComplete(Request *request)
{
- requestCompleted.emit(request, request->bufferMap_);
+ for (auto it : request->buffers()) {
+ Stream *stream = it.first;
+ Buffer *buffer = it.second;
+ if (stream->memoryType() == ExternalMemory)
+ stream->unmapBuffer(buffer);
+ }
+
+ requestCompleted.emit(request, request->buffers());
delete request;
}
diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp
index 98178e06..ed91be7f 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"
/**
@@ -476,6 +478,8 @@ std::unique_ptr<Buffer> Stream::createBuffer(unsigned int index)
* will return a null pointer when called on streams using the InternalMemory
* type.
*
+ * \sa Stream::mapBuffer()
+ *
* \return A newly created Buffer on success or nullptr otherwise
*/
std::unique_ptr<Buffer> Stream::createBuffer(const std::array<int, 3> &fds)
@@ -522,6 +526,86 @@ std::unique_ptr<Buffer> Stream::createBuffer(const std::array<int, 3> &fds)
*/
/**
+ * \brief Map a Buffer to a buffer memory index
+ * \param[in] buffer The buffer to map to a buffer memory index
+ *
+ * Streams configured to use externally allocated memory need to maintain a
+ * best-effort association between the memory area the \a buffer represents
+ * and the associated buffer memory in the Stream's pool.
+ *
+ * The buffer memory to use, once the \a buffer reaches the video device,
+ * is selected using the index assigned to the \a buffer and to minimize
+ * relocations in the V4L2 back-end, this operation provides a best-effort
+ * caching mechanism that associates to the dmabuf file descriptors contained
+ * in the \a buffer the index of the buffer memory that was lastly queued with
+ * those file descriptors set.
+ *
+ * If the Stream uses internally allocated memory, the index of the memory
+ * buffer to use will match the one request at Stream::createBuffer(unsigned int)
+ * time, and no mapping is thus required.
+ *
+ * \return The buffer memory index for the buffer on success, or a negative
+ * error code otherwise
+ * \retval -ENOMEM No buffer memory was available to map the buffer
+ */
+int Stream::mapBuffer(const Buffer *buffer)
+{
+ ASSERT(memoryType_ == ExternalMemory);
+
+ if (bufferCache_.empty())
+ return -ENOMEM;
+
+ const std::array<int, 3> &dmabufs = buffer->dmabufs();
+
+ /*
+ * Try to find a previously mapped buffer in the cache. If we miss, use
+ * the oldest entry in the cache.
+ */
+ auto map = std::find_if(bufferCache_.begin(), bufferCache_.end(),
+ [&](std::pair<std::array<int, 3>, unsigned int> &entry) {
+ return entry.first == dmabufs;
+ });
+ if (map == bufferCache_.end())
+ map = bufferCache_.begin();
+
+ /*
+ * Update the dmabuf file descriptors of the entry. We can't assume that
+ * identical file descriptor numbers refer to the same dmabuf object as
+ * it may have been closed and its file descriptor reused. We thus need
+ * to update the plane's internally cached mmap()ed memory.
+ */
+ unsigned int index = map->second;
+ BufferMemory *mem = &bufferPool_.buffers()[index];
+ mem->planes().clear();
+
+ for (unsigned int i = 0; i < dmabufs.size(); ++i) {
+ if (dmabufs[i] == -1)
+ break;
+
+ mem->planes().emplace_back();
+ mem->planes().back().setDmabuf(dmabufs[i], 0);
+ }
+
+ /* Remove the buffer from the cache and return its index. */
+ bufferCache_.erase(map);
+ return index;
+}
+
+/**
+ * \brief Unmap a Buffer from its buffer memory
+ * \param[in] buffer The buffer to unmap
+ *
+ * This method releases the buffer memory entry that was mapped by mapBuffer(),
+ * making it available for new mappings.
+ */
+void Stream::unmapBuffer(const Buffer *buffer)
+{
+ ASSERT(memoryType_ == ExternalMemory);
+
+ bufferCache_.emplace_back(buffer->dmabufs(), buffer->index());
+}
+
+/**
* \brief Create buffers for the stream
* \param[in] count The number of buffers to create
* \param[in] memory The stream memory type
@@ -536,6 +620,18 @@ void Stream::createBuffers(MemoryType memory, unsigned int count)
memoryType_ = memory;
bufferPool_.createBuffers(count);
+
+ /* Streams with internal memory usage do not need buffer mapping. */
+ if (memoryType_ == InternalMemory)
+ return;
+
+ /*
+ * Prepare for buffer mapping by adding all buffer memory entries to the
+ * cache.
+ */
+ bufferCache_.clear();
+ for (unsigned int i = 0; i < bufferPool_.count(); ++i)
+ bufferCache_.emplace_back(std::array<int, 3>{ -1, -1, -1 }, i);
}
/**