[libcamera-devel,v2,2/7] libcamera: Camera: Add signals for completion of metadata and partial result
diff mbox series

Message ID 20220812090838.1784703-3-hanlinchen@chromium.org
State New
Headers show
Series
  • Implement Android Partial Result Featrue
Related show

Commit Message

Hanlin Chen Aug. 12, 2022, 9:08 a.m. UTC
Allows pipelien handler to signal partial result completion by adding the
following signals to Camera:

Signal<Request *, ControlList *> metadataCompleted;
Signal<Request *, Result *> partialResultCompleted;

Together with the bufferCompleted signal, the pipeline handler is allowed to
return buffers, partial metadata and combination of both at any  stage of
processing.

Signed-off-by: Han-Lin Chen <hanlinchen@chromium.org>
---
 include/libcamera/camera.h                    |   2 +
 include/libcamera/internal/pipeline_handler.h |   4 +
 include/libcamera/request.h                   |  40 +++++
 src/libcamera/camera.cpp                      |  10 ++
 src/libcamera/pipeline_handler.cpp            | 121 ++++++++++++-
 src/libcamera/request.cpp                     | 170 +++++++++++++++++-
 6 files changed, 332 insertions(+), 15 deletions(-)

Patch
diff mbox series

diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
index 5aa4bf69..187e01d8 100644
--- a/include/libcamera/camera.h
+++ b/include/libcamera/camera.h
@@ -99,7 +99,9 @@  public:
 
 	const std::string &id() const;
 
+	Signal<Request *, Result *> partialResultCompleted;
 	Signal<Request *, FrameBuffer *> bufferCompleted;
+	Signal<Request *, ControlList *> metadataCompleted;
 	Signal<Request *> requestCompleted;
 	Signal<> disconnected;
 
diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
index c3e4c258..08519fb3 100644
--- a/include/libcamera/internal/pipeline_handler.h
+++ b/include/libcamera/internal/pipeline_handler.h
@@ -33,6 +33,7 @@  class FrameBuffer;
 class MediaDevice;
 class PipelineHandler;
 class Request;
+class Result;
 
 class PipelineHandler : public std::enable_shared_from_this<PipelineHandler>,
 			public Object
@@ -62,7 +63,9 @@  public:
 	void registerRequest(Request *request);
 	void queueRequest(Request *request);
 
+	void completeMetadata(Request *request, const ControlList &metadata);
 	bool completeBuffer(Request *request, FrameBuffer *buffer);
+	void completePartialResult(Request *request, Result &&result);
 	void completeRequest(Request *request);
 
 	const char *name() const { return name_; }
@@ -80,6 +83,7 @@  private:
 	void mediaDeviceDisconnected(MediaDevice *media);
 	virtual void disconnect();
 
+	void cancelRequest(Request *request);
 	void doQueueRequest(Request *request);
 	void doQueueRequests();
 
diff --git a/include/libcamera/request.h b/include/libcamera/request.h
index dffde153..ac520226 100644
--- a/include/libcamera/request.h
+++ b/include/libcamera/request.h
@@ -25,8 +25,43 @@  namespace libcamera {
 class Camera;
 class CameraControlValidator;
 class FrameBuffer;
+class Request;
 class Stream;
 
+class Result
+{
+public:
+	Result(Request *request);
+	Result(Result &&result);
+	~Result();
+
+	Request *request() const { return request_; }
+	const ControlList &metadata() const { return metadata_; }
+
+	const std::vector<FrameBuffer *> &buffers() const { return buffers_; }
+	int addBuffer(FrameBuffer *buffer);
+
+	template<typename T, typename V>
+	void set(const Control<T> &ctrl, const V &value)
+	{
+		return metadata_.set(ctrl, value);
+	}
+
+	void set(unsigned int id, const ControlValue &value);
+	void merge(const ControlList &source);
+
+	std::string toString() const;
+
+private:
+	LIBCAMERA_DISABLE_COPY(Result)
+
+	Request *request_;
+	ControlList metadata_;
+	std::vector<FrameBuffer *> buffers_;
+};
+
+std::ostream &operator<<(std::ostream &out, const Result &r);
+
 class Request : public Extensible
 {
 	LIBCAMERA_DECLARE_PRIVATE()
@@ -44,6 +79,7 @@  public:
 	};
 
 	using BufferMap = std::map<const Stream *, FrameBuffer *>;
+	using ResultList = std::vector<Result>;
 
 	Request(Camera *camera, uint64_t cookie = 0);
 	~Request();
@@ -53,9 +89,12 @@  public:
 	ControlList &controls() { return *controls_; }
 	ControlList &metadata() { return *metadata_; }
 	const BufferMap &buffers() const { return bufferMap_; }
+	ResultList &resultList() { return results_; }
+	Result *addResult(Result &&result);
 	int addBuffer(const Stream *stream, FrameBuffer *buffer,
 		      std::unique_ptr<Fence> fence = nullptr);
 	FrameBuffer *findBuffer(const Stream *stream) const;
+	const Stream *findStream(const FrameBuffer *buffer) const;
 
 	uint32_t sequence() const;
 	uint64_t cookie() const { return cookie_; }
@@ -71,6 +110,7 @@  private:
 	ControlList *controls_;
 	ControlList *metadata_;
 	BufferMap bufferMap_;
+	ResultList results_;
 
 	const uint64_t cookie_;
 	Status status_;
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index 4d3523e2..a5fb9b33 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -765,6 +765,16 @@  const std::string &Camera::id() const
  * completed
  */
 
+/**
+ * \var Camera::metadataCompleted
+ * \brief Signal emitted when a partial metadata for a request is completed
+ */
+
+/**
+ * \var Camera::partialResultCompleted
+ * \brief Signal emitted when a partial result for a request is completed
+ */
+
 /**
  * \var Camera::requestCompleted
  * \brief Signal emitted when a request queued to the camera has completed
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index 7c180a8e..6608c210 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -305,8 +305,9 @@  void PipelineHandler::stop(Camera *camera)
 		Request *request = waitingRequests_.front();
 		waitingRequests_.pop();
 
-		request->_d()->cancel();
-		completeRequest(request);
+		cancelRequest(request);
+		request->_d()->complete();
+		camera->requestCompleted.emit(request);
 	}
 
 	/* Make sure no requests are pending. */
@@ -402,17 +403,30 @@  void PipelineHandler::doQueueRequest(Request *request)
 	request->_d()->sequence_ = data->requestSequence_++;
 
 	if (request->_d()->cancelled_) {
+		cancelRequest(request);
 		completeRequest(request);
 		return;
 	}
 
 	int ret = queueRequestDevice(camera, request);
 	if (ret) {
-		request->_d()->cancel();
+		cancelRequest(request);
 		completeRequest(request);
 	}
 }
 
+/**
+ * \brief Cancel buffers of a request and complete the all buffers
+ */
+void PipelineHandler::cancelRequest(Request *request)
+{
+	request->_d()->cancel();
+	for (auto it : request->buffers()) {
+		FrameBuffer *buffer = it.second;
+		completeBuffer(request, buffer);
+	}
+}
+
 /**
  * \brief Queue prepared requests to the device
  *
@@ -453,9 +467,10 @@  void PipelineHandler::doQueueRequests()
  * \param[in] request The request the buffer belongs to
  * \param[in] buffer The buffer that has completed
  *
- * This function shall be called by pipeline handlers to signal completion of
+ * This function could be called by pipeline handlers to signal completion of
  * the \a buffer part of the \a request. It notifies applications of buffer
- * completion and updates the request's internal buffer tracking. The request
+ * completion and updates the request's internal buffer tracking. The function
+ * notify completion of a partial result including the buffer. The request
  * is not completed automatically when the last buffer completes to give
  * pipeline handlers a chance to perform any operation that may still be
  * needed. They shall complete requests explicitly with completeRequest().
@@ -466,10 +481,75 @@  void PipelineHandler::doQueueRequests()
  * otherwise
  */
 bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer)
+{
+	Result result(request);
+	result.addBuffer(buffer);
+	completePartialResult(request, std::move(result));
+
+	return request->_d()->hasPendingBuffers();
+}
+
+/**
+ * \brief Complete part of metadata for a request
+ * \param[in] request The request the buffer belongs to
+ * \param[in] metadata The partial metadata that has completed
+ *
+ * This function could be called by pipeline handlers to signal completion of
+ * the \a metadata part of the \a request. It notifies applications of metadata
+ * completion. The function notify completion of a partial result including the
+ * metadata. The request is not completed automatically when the last metadata
+ * completes to give pipeline handlers a chance to perform any operation that
+ * may still be needed. They shall complete requests explicitly with
+ * completeRequest().
+ *
+ * \context This function shall be called from the CameraManager thread.
+ */
+void PipelineHandler::completeMetadata(Request *request, const ControlList &metadata)
+{
+	Result result = Result(request);
+	result.merge(metadata);
+	completePartialResult(request, std::move(result));
+}
+
+/**
+ * \brief Complete part of metadata and buffer for a request
+ * \param[in] request The request the buffer belongs to
+ * \param[in] result The partial result that has completed
+ *
+ * This function could be called by pipeline handlers to signal completion of
+ * the \a result part of the \a request. It notifies applications of partial
+ * completion. The function notify completion of buffers and metadata included
+ * in the result. The request is not completed automatically when the last
+ * result completes to give pipeline handlers a chance to perform any operation
+ * that may still be needed. They shall complete requests explicitly with
+ * completeRequest(). The function only accepts rvalue of a Result type and
+ * its interval content will be moved to the internal store to avoid copying
+ * big metadata.
+ *
+ * \context This function shall be called from the CameraManager thread.
+ */
+void PipelineHandler::completePartialResult(Request *request, Result &&result)
 {
 	Camera *camera = request->_d()->camera();
-	camera->bufferCompleted.emit(request, buffer);
-	return request->_d()->completeBuffer(buffer);
+	Result *movedResult = request->addResult(std::move(result));
+
+	ASSERT(movedResult->request() == request);
+	ASSERT(!movedResult->buffers().empty() ||
+	       !movedResult->metadata().empty());
+
+	for (auto buffer : movedResult->buffers()) {
+		request->_d()->completeBuffer(buffer);
+		camera->bufferCompleted.emit(request, buffer);
+	}
+
+	if (!movedResult->metadata().empty()) {
+		request->metadata().merge(movedResult->metadata());
+		camera->metadataCompleted.emit(
+			request,
+			&const_cast<ControlList &>(movedResult->metadata()));
+	}
+
+	camera->partialResultCompleted.emit(request, movedResult);
 }
 
 /**
@@ -494,6 +574,33 @@  void PipelineHandler::completeRequest(Request *request)
 
 	Camera::Private *data = camera->_d();
 
+	/*
+	 * Collect metadata which is not yet completed by the Camera, and
+	 * create one partial result to cover the missing metadata before
+	 * completing the whole request. This guarantees the aggregation of
+	 * metadata in completed partial results equals to the global metadata
+	 * in the request.
+	 *
+	 * \todo: Forbid merging metadata into request.metadata() directly and
+	 * force calling completeMetadata() and completePartialResult() to
+	 * report metadata.
+	 */
+	std::unordered_set<unsigned int> completedMetadata;
+	for (auto &result : request->resultList()) {
+		for (auto &[id, _] : result.metadata())
+			completedMetadata.insert(id);
+	}
+
+	ControlList &requestMetadata = request->metadata();
+	if (requestMetadata.size() > completedMetadata.size()) {
+		Result result(request);
+		for (auto &[id, value] : requestMetadata)
+			if (!completedMetadata.count(id))
+				result.set(id, value);
+
+		completePartialResult(request, std::move(result));
+	}
+
 	auto iter = data->queuedRequests_.begin();
 	while (iter != data->queuedRequests_.end()) {
 		if ((*iter)->status() != Request::RequestPending) {
diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
index d2af1d22..239c29cf 100644
--- a/src/libcamera/request.cpp
+++ b/src/libcamera/request.cpp
@@ -126,15 +126,10 @@  void Request::Private::complete()
 
 void Request::Private::doCancelRequest()
 {
-	Request *request = _o<Request>();
-
-	for (FrameBuffer *buffer : pending_) {
+	for (FrameBuffer *buffer : pending_)
 		buffer->_d()->cancel();
-		camera_->bufferCompleted.emit(request, buffer);
-	}
 
 	cancelled_ = true;
-	pending_.clear();
 	notifiers_.clear();
 	timer_.reset();
 }
@@ -144,8 +139,8 @@  void Request::Private::doCancelRequest()
  *
  * Mark the request and its associated buffers as cancelled and complete it.
  *
- * Set each pending buffer in error state and emit the buffer completion signal
- * before completing the Request.
+ * Set each pending buffer in error state. The pipeline handler shall complete
+ * the cancelled buffers to notice the application.
  */
 void Request::Private::cancel()
 {
@@ -323,6 +318,11 @@  void Request::Private::timeout()
  * \brief A map of Stream to FrameBuffer pointers
  */
 
+/**
+ * \typedef Request::ResultList
+ * \brief A list partial results associated with a request
+ */
+
 /**
  * \class Request
  * \brief A frame capture request
@@ -394,6 +394,7 @@  void Request::reuse(ReuseFlag flags)
 
 	status_ = RequestPending;
 
+	results_.clear();
 	controls_->clear();
 	metadata_->clear();
 }
@@ -424,6 +425,12 @@  void Request::reuse(ReuseFlag flags)
  * \return The map of Stream to FrameBuffer
  */
 
+/**
+ * \fn Request::resultList()
+ * \brief Retrieve the request's partial results
+ * \return A reference to the list of results that associates with the request
+ */
+
 /**
  * \brief Add a FrameBuffer with its associated Stream to the Request
  * \param[in] stream The stream the buffer belongs to
@@ -489,6 +496,20 @@  int Request::addBuffer(const Stream *stream, FrameBuffer *buffer,
 	return 0;
 }
 
+/**
+ * \brief Add result into the internal result list
+ * \param[in] result The result to add into the request
+ *
+ * The function only accepts rvalue and moves its content into the request
+ *
+ * \return The result moved into the request
+ */
+Result *Request::addResult(Result &&result)
+{
+	results_.emplace_back(std::move(result));
+	return &results_.back();
+}
+
 /**
  * \var Request::bufferMap_
  * \brief Mapping of streams to buffers for this request
@@ -513,6 +534,21 @@  FrameBuffer *Request::findBuffer(const Stream *stream) const
 	return it->second;
 }
 
+/**
+ * \brief Return the stream associated with a buffer
+ * \param[in] buffer The buffer the stream is associated to
+ * \return The stream associated with the buffer, or nullptr if the buffer is
+ * not part of this request
+ */
+const Stream *Request::findStream(const FrameBuffer *buffer) const
+{
+	for (auto &[key, value] : bufferMap_)
+		if (buffer == value)
+			return key;
+
+	return nullptr;
+}
+
 /**
  * \fn Request::metadata()
  * \brief Retrieve the request's metadata
@@ -606,4 +642,122 @@  std::ostream &operator<<(std::ostream &out, const Request &r)
 	return out;
 }
 
+/**
+ * \class Result
+ * \brief A partial result of a frame capture request
+ *
+ * A Result allows pipeline handler to report partial results to the application
+ */
+
+/**
+ * \brief Create a partial result for a capture request
+ * \param[in] request The request the result associated to
+ */
+Result::Result(Request *request)
+	: request_(request)
+{
+}
+
+/**
+ * \brief Move constructor of a Result
+ * \param[in] result The other result
+ */
+Result::Result(Result &&result) = default;
+
+Result::~Result() = default;
+
+/**
+ * \fn Result::request()
+ * \brief Retrieve the result's associated request
+ * \return The Request pointer associated with the result
+ */
+
+/**
+ * \fn Result::metadata()
+ * \brief Retrieve the result's metadata
+ * \return The metadata contained in the result
+ */
+
+/**
+ * \fn Result::buffers()
+ * \brief Retrieve the result's buffers
+ * \return The buffers contained in the result
+ */
+
+/**
+ * \fn Result::set(const Control<T> &ctrl, const V &value)
+ * \brief Set the control \a ctrl value to \a value into metadata
+ * \param[in] ctrl The control
+ * \param[in] value The control value
+ */
+
+/**
+ * \brief Add a FrameBuffer with its associated Stream to the Result
+ * \param[in] buffer The FrameBuffer to add to the result
+ *
+ * \return 0 on success or a negative error code otherwise
+ * \retval -EINVAL The buffer does not reference a valid Stream in the request
+ */
+int Result::addBuffer(FrameBuffer *buffer)
+{
+	if (!buffer || !request_->findStream(buffer)) {
+		LOG(Request, Error) << "Invalid buffer reference";
+		return -EINVAL;
+	}
+
+	buffers_.emplace_back(buffer);
+	return 0;
+}
+
+/**
+ * \brief Set the control \a id to \a value
+ * \param[in] id The control id
+ * \param[in] value The control value
+ */
+void Result::set(unsigned int id, const ControlValue &value)
+{
+	metadata_.set(id, value);
+}
+
+/**
+ * \brief Merge the \a source into the metadata of the result
+ * \param[in] source The ControlList to merge into this metadata
+ */
+void Result::merge(const ControlList &source)
+{
+	metadata_.merge(source);
+}
+
+/**
+ * \brief Generate a string representation of the Result internals
+ *
+ * This function facilitates debugging of Result state while it is used
+ * internally within libcamera.
+ *
+ * \return A string representing the current state of the result
+ */
+std::string Result::toString() const
+{
+	std::stringstream ss;
+	ss << *this;
+
+	return ss.str();
+}
+
+/**
+ * \brief Insert a text representation of a Result into an output stream
+ * \param[in] out The output stream
+ * \param[in] r The Result
+ * \return The output stream \a out
+ */
+std::ostream &operator<<(std::ostream &out, const Result &r)
+{
+	/* Example Output: Result(55:1/2) */
+	out << "Result(" << r.request()->sequence() << ":"
+	    << r.buffers().size() << "/"
+	    << const_cast<Result &>(r).metadata().size() << ")";
+
+	return out;
+}
+
 } /* namespace libcamera */