@@ -28,6 +28,7 @@
namespace libcamera {
+class Fence;
class FrameBuffer;
class FrameBufferAllocator;
class PipelineHandler;
@@ -152,6 +153,8 @@ public:
int start(const ControlList *controls = nullptr);
int stop();
+ int addBuffer(const Stream *stream, FrameBuffer *buffer, std::unique_ptr<Fence> &&fence = {});
+
private:
LIBCAMERA_DISABLE_COPY(Camera)
@@ -15,14 +15,17 @@
#include <stdint.h>
#include <string>
#include <unordered_map>
+#include <vector>
#include <libcamera/base/class.h>
+#include <libcamera/base/event_notifier.h>
#include <libcamera/camera.h>
namespace libcamera {
class CameraControlValidator;
+class Fence;
class PipelineHandler;
class Stream;
@@ -57,6 +60,15 @@ private:
struct StreamData {
bool active = false;
+ std::vector<FrameBuffer *> buffers;
+ };
+
+ struct PendingFence {
+ EventNotifier notifier;
+ const Stream *stream;
+ FrameBuffer *buffer;
+
+ PendingFence(const Stream *s, FrameBuffer *b);
};
bool isAcquired() const;
@@ -74,11 +86,14 @@ private:
std::string id_;
std::set<Stream *> streams_;
std::unordered_map<const Stream *, StreamData> streamData_;
+ std::list<PendingFence> pendingFences_;
bool disconnected_;
std::atomic<State> state_;
std::unique_ptr<CameraControlValidator> validator_;
+
+ friend PipelineHandler;
};
} /* namespace libcamera */
@@ -27,6 +27,7 @@ class Camera;
class CameraConfiguration;
class DeviceEnumerator;
class DeviceMatch;
+class Fence;
class FrameBuffer;
class MediaDevice;
class PipelineHandler;
@@ -69,6 +70,10 @@ public:
void completeRequest(Request *request);
void cancelRequest(Request *request);
+ void addBuffer(Camera *camera,
+ const Stream *stream, FrameBuffer *buffer,
+ std::unique_ptr<Fence> &&fence);
+
std::string configurationFile(const std::string &subdir,
const std::string &name,
bool silent = false) const;
@@ -27,6 +27,7 @@
#include "libcamera/internal/camera.h"
#include "libcamera/internal/camera_controls.h"
+#include "libcamera/internal/framebuffer.h"
#include "libcamera/internal/pipeline_handler.h"
#include "libcamera/internal/request.h"
@@ -746,6 +747,13 @@ void Camera::Private::setState(State state)
{
state_.store(state, std::memory_order_release);
}
+
+Camera::Private::PendingFence::PendingFence(const Stream *s, FrameBuffer *b)
+ : notifier(b->_d()->fence()->fd().get(), EventNotifier::Read),
+ stream(s),
+ buffer(b)
+{
+}
#endif /* __DOXYGEN_PUBLIC__ */
/**
@@ -1061,6 +1069,9 @@ int Camera::release()
d->pipe_->invokeMethod(&PipelineHandler::release,
ConnectionTypeBlocking, this);
+ // \todo clear buffer pool
+ // \todo empty `pendingBuffers_` and report "cancelled" fences
+
d->setState(Private::CameraAvailable);
return 0;
@@ -1448,6 +1459,9 @@ int Camera::start(const ControlList *controls)
* This function stops capturing and processing requests immediately. All
* pending requests are cancelled and complete synchronously in an error state.
*
+ * All buffers in the camera's buffer pool are returned via the Camera::bufferCompleted
+ * event synchronously with the \a request parameter equal to \a nullptr.
+ *
* \context This function may be called in any camera state as defined in \ref
* camera_operation, and shall be synchronized by the caller with other
* functions that affect the camera state. If called when the camera isn't
@@ -1486,6 +1500,43 @@ int Camera::stop()
return 0;
}
+/**
+ * \fn Camera::addBuffer()
+ * \brief Add a buffer to buffer pool of the camera
+ * \param[in] stream The stream
+ * \param[in] buffer The buffer
+ * \param[in] fence The fence for \a buffer
+ *
+ * \context This function may only be called when the camera is
+ * in the Running state as defined in \ref camera_operation.
+ *
+ * \return 0 on success or a negative error code otherwise
+ *
+ * \internal
+ * \todo Add `addBuffers()` that accepts a list of (stream, buffer, fence) tuples.
+ */
+int Camera::addBuffer(const Stream *stream, FrameBuffer *buffer, std::unique_ptr<Fence> &&fence)
+{
+ Private *const d = _d();
+
+ int ret = d->isAccessAllowed(Private::CameraRunning);
+ if (ret < 0)
+ return ret;
+
+ auto it = d->streamData_.find(stream);
+ if (it == d->streamData_.end() || !it->second.active)
+ return -EINVAL;
+ if (!buffer)
+ return -EINVAL;
+ if (buffer->_d()->fence())
+ return -EINVAL;
+
+ d->pipe_->invokeMethod(&PipelineHandler::addBuffer, ConnectionTypeQueued,
+ this, stream, buffer, std::move(fence));
+
+ return 0;
+}
+
/**
* \brief Handle request completion and notify application
* \param[in] request The request that has completed
@@ -22,6 +22,7 @@
#include "libcamera/internal/camera.h"
#include "libcamera/internal/camera_manager.h"
#include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/framebuffer.h"
#include "libcamera/internal/media_device.h"
#include "libcamera/internal/request.h"
#include "libcamera/internal/tracepoints.h"
@@ -394,6 +395,24 @@ void PipelineHandler::stop(Camera *camera)
doQueueRequest(request);
}
+ const auto returnBuffer = [&](FrameBuffer *buffer) {
+ ASSERT(!buffer->_d()->stream_);
+ buffer->_d()->cancel();
+ camera->bufferCompleted.emit(nullptr, buffer);
+ };
+
+ for (auto &pf : data->pendingFences_)
+ returnBuffer(pf.buffer);
+
+ data->pendingFences_.clear();
+
+ for (auto &[stream, streamData] : data->streamData_) {
+ for (FrameBuffer *buffer : streamData.buffers)
+ returnBuffer(buffer);
+
+ streamData.buffers.clear();
+ }
+
/* Make sure no requests are pending. */
ASSERT(data->queuedRequests_.empty());
ASSERT(data->waitingRequests_.empty());
@@ -611,6 +630,75 @@ void PipelineHandler::cancelRequest(Request *request)
completeRequest(request);
}
+/**
+ * \fn PipelineHandler::addBuffer()
+ * \brief Add buffers to the buffer pool of the camera
+ * \param[in] camera The camera
+ * \param[in] stream The stream of \a camera
+ * \param[in] buffer The buffer
+ * \param[in] fence The fence for \a buffer
+ *
+ * \context This function may only be called from the CameraManager thread.
+ */
+void PipelineHandler::addBuffer(Camera *camera,
+ const Stream *stream, FrameBuffer *buffer,
+ std::unique_ptr<Fence> &&fence)
+{
+ Camera::Private *const d = camera->_d();
+
+ auto it = d->streamData_.find(stream);
+ ASSERT(it != d->streamData_.end());
+
+ [[maybe_unused]] const auto checkUnique = [&] {
+ for (const auto &[_, data] : d->streamData_) {
+ for (const auto *b : data.buffers) {
+ if (b == buffer)
+ return false;
+ }
+ }
+
+ for (const auto &pf : d->pendingFences_) {
+ if (pf.buffer == buffer)
+ return false;
+
+ if (fence && fence->isValid()) {
+ if (pf.buffer->_d()->fence()->fd().get() == fence->fd().get())
+ return false;
+ }
+ }
+
+ return true;
+ };
+ ASSERT(checkUnique() && "buffer or fence is already present in the pool");
+
+ if (fence && fence->isValid()) {
+ buffer->_d()->setFence(std::move(fence));
+
+ auto it2 = d->pendingFences_.emplace(
+ d->pendingFences_.end(),
+ stream,
+ buffer
+ );
+
+ LOG(Pipeline, Debug)
+ << "Waiting on fence:" << buffer->_d()->fence()->fd().get()
+ << " for stream:" << stream << " buffer:" << buffer;
+
+ it2->notifier.activated.connect(this, [=] {
+ LOG(Pipeline, Debug)
+ << "Activated fence:" << it2->buffer->_d()->fence()->fd().get()
+ << " for stream:" << it2->stream << " buffer:" << it2->buffer;
+
+ std::ignore = it2->buffer->releaseFence();
+ it->second.buffers.push_back(it2->buffer);
+ d->pendingFences_.erase(it2);
+ /* Lambda is now destroy, no captured variable should be accessed. */
+ });
+ } else {
+ it->second.buffers.push_back(buffer);
+ }
+}
+
/**
* \brief Retrieve the absolute path to a platform configuration file
* \param[in] subdir The pipeline handler specific subdirectory name
Use the recently added `StreamData` to store a set of buffers for each stream. Introduce the `Camera::addBuffer()` method that can be used by an application to add buffers. Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> --- include/libcamera/camera.h | 3 + include/libcamera/internal/camera.h | 15 ++++ include/libcamera/internal/pipeline_handler.h | 5 ++ src/libcamera/camera.cpp | 51 +++++++++++ src/libcamera/pipeline_handler.cpp | 88 +++++++++++++++++++ 5 files changed, 162 insertions(+)