@@ -147,6 +147,7 @@ public:
std::unique_ptr<Request> createRequest(uint64_t cookie = 0);
int queueRequest(Request *request);
+ int applyControls(ControlList &&controls);
int start(const ControlList *controls = nullptr);
int stop();
@@ -38,6 +38,7 @@ public:
std::list<Request *> queuedRequests_;
std::deque<Request *> waitingRequests_;
+ ControlList pendingControls_;
ControlInfoMap controlInfo_;
ControlList properties_;
@@ -57,6 +57,7 @@ public:
void registerRequest(Request *request);
void queueRequest(Request *request);
+ int applyControls(Camera *camera, ControlList &&controls);
bool completeBuffer(Request *request, FrameBuffer *buffer);
void completeRequest(Request *request);
@@ -75,6 +76,12 @@ protected:
void hotplugMediaDevice(MediaDevice *media);
virtual int queueRequestDevice(Camera *camera, Request *request) = 0;
+
+ virtual int applyControlsDevice([[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,14 @@ Camera::Private::~Private()
* queued requests was reached.
*/
+/**
+ * \var Camera::Private::pendingControls_
+ * \brief The list of pending controls
+ *
+ * This list tracks the controls that need to be applied to the device with the
+ * next request going to PipelineHandler::queueRequestDevice().
+ */
+
/**
* \var Camera::Private::controlInfo_
* \brief The set of controls supported by the camera
@@ -1374,6 +1382,51 @@ int Camera::queueRequest(Request *request)
return 0;
}
+/**
+ * \brief Apply controls immediately
+ * \param[in] controls The list of controls to apply
+ *
+ * This function tries to ensure that the controls in \a controls are applied
+ * to the camera as soon as possible.
+ *
+ * The exact guarantees are camera dependent, but it is guaranteed that the
+ * controls will be applied no later than if they were part of the next \ref Request "request"
+ * that the application \ref Camera::queueRequest() "queues".
+ *
+ * \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::applyControls(ControlList &&controls)
+{
+ Private *const d = _d();
+
+ /*
+ * \todo What to do if not running? Should it be stored and merged into the "start" `ControlList`?
+ * If yes, should that list of pending controls be overwritten/updated when stopped?
+ * And should it be cleared in `Camera::release()`?
+ */
+
+ int ret = d->isAccessAllowed(Private::CameraRunning);
+ if (ret < 0)
+ return ret;
+
+ if (controls.empty())
+ return 0;
+
+ patchControlList(controls);
+
+ /*
+ * \todo Or `ConnectionTypeBlocking` to get the return value?
+ */
+ d->pipe_->invokeMethod(&PipelineHandler::applyControls, ConnectionTypeQueued, this, std::move(controls));
+
+ return 0;
+}
+
/**
* \brief Start capture from camera
* \param[in] controls Controls to be applied before starting the Camera
@@ -387,6 +387,12 @@ void PipelineHandler::stop(Camera *camera)
ASSERT(data->queuedRequests_.empty());
ASSERT(data->waitingRequests_.empty());
+ /*
+ * \todo Or not? This probably needs discussions regarding
+ * the expected state of controls after a stop-start sequence.
+ */
+ data->pendingControls_.clear();
+
data->requestSequence_ = 0;
}
@@ -466,6 +472,54 @@ void PipelineHandler::queueRequest(Request *request)
request->_d()->prepare(300ms);
}
+/**
+ * \brief Apply controls immediately
+ * \param[in] camera The camera
+ * \param[in] controls The controls to apply
+ *
+ * This function tries to apply \a controls immediately to the device by
+ * calling applyControlsDevice(). If that fails, then a fallback mechanism
+ * is used to ensure that \a controls will be merged into the control list
+ * of the next request submitted to the pipeline handler.
+ *
+ * This function also ensures that requests in Camera::Private::waitingRequests_
+ * will not override the fast-tracked controls.
+ *
+ * \context This function is called from the CameraManager thread.
+ */
+int PipelineHandler::applyControls(Camera *camera, ControlList &&controls)
+{
+ Camera::Private *data = camera->_d();
+ int res = applyControlsDevice(camera, controls);
+
+ /*
+ * Prevent later requests from overriding the fast-tracked controls.
+ * \todo This is unfortunately slow.
+ */
+ for (const auto &[k, v] : controls) {
+ for (Request *r : data->waitingRequests_)
+ r->controls().erase(k);
+ }
+
+ if (res < 0) {
+ /*
+ * \todo Always fall back or only if EOPNOTSUPP is returned?
+ * Possibly there could be some way for the user to influence
+ * the fallback logic (e.g. `flags` argument for `Camera::applyControls()`).
+ */
+ if (res != -EOPNOTSUPP)
+ LOG(Pipeline, Debug) << "Fast tracking controls failed: " << res;
+
+ /*
+ * Fall back to adding the controls to the next request that enters the
+ * pipeline handler. See PipelineHandler::doQueueRequest().
+ */
+ data->pendingControls_.merge(std::move(controls), ControlList::MergePolicy::OverwriteExisting);
+ }
+
+ return 0;
+}
+
/**
* \brief Queue one requests to the device
*/
@@ -484,9 +538,49 @@ void PipelineHandler::doQueueRequest(Request *request)
return;
}
+ if (!data->pendingControls_.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.
+ *
+ * For older requests, `PipelineHandler::applyControls()` has already removed
+ * the controls that should be overridden.
+ *
+ * \todo How to handle (if at all) conflicting controls?
+ * \todo How to handle failure of `queueRequestDevice()`?
+ * After the merge it becomes impossible to retrieve the pending controls
+ * from `request->controls()`. Making a copy in such a hot-path is not ideal.
+ */
+ request->controls().merge(std::move(data->pendingControls_), ControlList::MergePolicy::KeepExisting);
+ data->pendingControls_.clear();
+ }
+
int ret = queueRequestDevice(camera, request);
- if (ret)
+ if (ret) {
+ /*
+ * \todo What to do now?
+ *
+ * The original `pendingControls_` is not easily recoverable.
+ *
+ * Initially
+ * request->controls() = A u B
+ * pendingControls_ = A' u C
+ *
+ * then after the above merge
+ * request->controls() = A u B u C
+ * pendingControls_ = A'
+ *
+ * so recovering `pendingControls_` without a copy of at least
+ * `B` or `C` does not appear easy.
+ *
+ * But sometimes recovering is not even desirable: assume that the controls
+ * in `pendingControls_` causes the failure. In that case keeping them around
+ * will cause every single subsequent request to fail.
+ */
+
cancelRequest(request);
+ }
}
/**
@@ -532,6 +626,20 @@ void PipelineHandler::doQueueRequests(Camera *camera)
* \return 0 on success or a negative error code otherwise
*/
+/**
+ * \fn PipelineHandler::applyControlsDevice()
+ * \brief Apply controls immediately
+ * \param[in] camera The camera
+ * \param[in] controls The controls to apply
+ *
+ * This function applies \a controls to \a camera immediately.
+ *
+ * \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::applyControls()` whose purpose is to apply controls as soon as possible, without going through `Request::controls()`. A new virtual function `PipelineHandler::applyControlsDevice()` 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 request sent to the pipeline handler (`Camera::Private::waitingRequests_`). Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> --- include/libcamera/camera.h | 1 + include/libcamera/internal/camera.h | 1 + include/libcamera/internal/pipeline_handler.h | 7 ++ src/libcamera/camera.cpp | 53 +++++++++ src/libcamera/pipeline_handler.cpp | 110 +++++++++++++++++- 5 files changed, 171 insertions(+), 1 deletion(-)