@@ -147,6 +147,7 @@ public:
std::unique_ptr<Request> createRequest(uint64_t cookie = 0);
int queueRequest(Request *request);
+ int queueControls(ControlList &&controls);
int start(const ControlList *controls = nullptr);
int stop();
@@ -38,6 +38,7 @@ public:
std::list<Request *> queuedRequests_;
std::queue<Request *> waitingRequests_;
+ std::queue<ControlList> queuedControls_;
ControlInfoMap controlInfo_;
ControlList properties_;
@@ -57,6 +57,7 @@ public:
void registerRequest(Request *request);
void queueRequest(Request *request);
+ int queueControls(Camera *camera, ControlList controls);
bool completeBuffer(Request *request, FrameBuffer *buffer);
void completeRequest(Request *request);
@@ -76,6 +77,12 @@ protected:
unsigned int useCount() const { return useCount_; }
virtual int queueRequestDevice(Camera *camera, Request *request) = 0;
+
+ virtual int queueControlsDevice([[maybe_unused]] Camera *camera, [[maybe_unused]] const ControlList &controls)
+ {
+ return -EOPNOTSUPP;
+ }
+
virtual void stopDevice(Camera *camera) = 0;
virtual bool acquireDevice(Camera *camera);
@@ -637,6 +637,16 @@ Camera::Private::~Private()
* queued requests was reached.
*/
+/**
+ * \var Camera::Private::queuedControls_
+ * \brief The queue of pending control lists
+ *
+ * This queue maintains a list of all the control lists that need to be sent
+ * to the pipeline handler with subsequent requests. The top item in the queue
+ * will always be sent with the next request going to
+ * PipelineHandler::queueRequestDevice().
+ */
+
/**
* \var Camera::Private::controlInfo_
* \brief The set of controls supported by the camera
@@ -1378,6 +1388,57 @@ int Camera::queueRequest(Request *request)
return 0;
}
+/**
+ * \brief Queue controls to be applied as soon as possible
+ * \param[in] controls The list of controls to queue
+ *
+ * This function tries to ensure that the controls in \a controls are applied
+ * to the camera as soon as possible. If there are still pending controls waiting
+ * to be applied (because of previous calls to Camera::queueControls), then
+ * these controls will be applied as soon as possible on a frame after those.
+ *
+ * The exact guarantees are camera dependent, but it is guaranteed that the
+ * controls will be applied no later than with the next \ref Request"request"
+ * that the application \ref Camera::queueRequest() "queues" (after any requests
+ * have been *used up" for sending previously queued controls).
+ *
+ * \context This function is \threadsafe. It 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
+ * \retval -ENODEV The camera has been disconnected from the system
+ * \retval -EACCES The camera is not running
+ */
+int Camera::queueControls(ControlList &&controls)
+{
+ Private *const d = _d();
+
+ /*
+ * Like requests, controls can't be queued if the camera is not running.
+ * Controls can be applied immediately when the camera starts using the
+ * Camera::Start method.
+ */
+
+ int ret = d->isAccessAllowed(Private::CameraRunning);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * We want to be able to queue empty control lists, as this gives a way of
+ * forcing another frame with the same controls as last time, before queueing
+ * another control list that might change them again.
+ */
+
+ patchControlList(controls);
+
+ /*
+ * \todo Or `ConnectionTypeBlocking` to get the return value?
+ */
+ d->pipe_->invokeMethod(&PipelineHandler::queueControls, ConnectionTypeQueued, this, std::move(controls));
+
+ return 0;
+}
+
/**
* \brief Start capture from camera
* \param[in] controls Controls to be applied before starting the Camera
@@ -398,6 +398,12 @@ void PipelineHandler::stop(Camera *camera)
ASSERT(data->queuedRequests_.empty());
ASSERT(data->waitingRequests_.empty());
+ /*
+ * Clear out any unapplied controls. If an application wants to be
+ * sure controls have been applied, it should wait before stopping.
+ */
+ data->queuedControls_ = {};
+
data->requestSequence_ = 0;
}
@@ -477,6 +483,49 @@ void PipelineHandler::queueRequest(Request *request)
request->_d()->prepare(300ms);
}
+/**
+ * \brief Queue controls to apply as soon as possible
+ * \param[in] camera The camera
+ * \param[in] controls The controls to apply
+ *
+ * This function tries to queue \a controls immediately to the device by
+ * calling queueControlsDevice(). If that fails, then a fallback mechanism
+ * is used to ensure that \a controls will be merged into the control list
+ * of the next available request submitted to the pipeline handler.
+ *
+ * \context This function is called from the CameraManager thread.
+ */
+int PipelineHandler::queueControls(Camera *camera, ControlList controls)
+{
+ Camera::Private *data = camera->_d();
+ int ret = queueControlsDevice(camera, controls);
+
+ /*
+ * Don't worry about later request's controls overriding the ones
+ * sent here - the application needs to deal with that.
+ */
+
+ if (ret == -EOPNOTSUPP) {
+ /*
+ * Fall back to adding the controls to the next request that enters the
+ * pipeline handler. See PipelineHandler::doQueueRequest().
+ */
+ data->queuedControls_.push(std::move(controls));
+
+ /* Counts as "success". */
+ ret = 0;
+
+ } else if (ret < 0) {
+ /*
+ * The pipeline handler is claiming to support queueControlsDevice,
+ * but it has failed. This is an error.
+ */
+ LOG(Pipeline, Debug) << "Fast tracking controls failed: " << res;
+ }
+
+ return ret;
+}
+
/**
* \brief Queue one requests to the device
*/
@@ -495,9 +544,21 @@ void PipelineHandler::doQueueRequest(Request *request)
return;
}
+ if (!data->queuedControls_.empty()) {
+ /*
+ * Note that `ControlList::MergePolicy::KeepExisting` is used. This is
+ * needed to ensure that if `request` is newer than pendingControls_,
+ * then its controls take precedence.
+ */
+ request->controls().merge(data->queuedControls_.front(),
+ ControlList::MergePolicy::KeepExisting);
+ }
+
int ret = queueRequestDevice(camera, request);
if (ret)
cancelRequest(request);
+ else if (!data->queuedControls_.empty())
+ data->queuedControls_.pop();
}
/**
@@ -543,6 +604,21 @@ void PipelineHandler::doQueueRequests(Camera *camera)
* \return 0 on success or a negative error code otherwise
*/
+/**
+ * \fn PipelineHandler::queueControlsDevice()
+ * \brief Queue controls to be applied as soon as possible
+ * \param[in] camera The camera
+ * \param[in] controls The controls to apply
+ *
+ * This function queues \a controls to \a camera so that they can be
+ * applied as soon as possible
+ *
+ * \context This function is called from the CameraManager thread.
+ *
+ * \return 0 on success or a negative error code otherwise
+ * \return -EOPNOTSUPP if fast-tracking controls is not supported
+ */
+
/**
* \brief Complete a buffer for a request
* \param[in] request The request the buffer belongs to
Add `Camera::queueControls()` whose purpose is to apply controls as soon as possible, without going through `Request::controls()`. A new virtual function `PipelineHandler::queueControlsDevice()` is provided for pipeline handler to implement fast-tracked application of controls. If the pipeline handler does not implement that functionality, or it fails, then a fallback mechanism is used. The controls will be saved for later, and they will be merged into the control list of the next available request sent to the pipeline handler (`Camera::Private::waitingRequests_`). This patch is derived directly from Barnabas's previous verion that implemented the same idea but with a single ControlList, rather than allowing multiple ControlLists to be queued up for consecutive frames. Signed-off-by: David Plowman <david.plowman@raspberrypi.com> --- include/libcamera/camera.h | 1 + include/libcamera/internal/camera.h | 1 + include/libcamera/internal/pipeline_handler.h | 7 ++ src/libcamera/camera.cpp | 61 +++++++++++++++ src/libcamera/pipeline_handler.cpp | 76 +++++++++++++++++++ 5 files changed, 146 insertions(+)