[libcamera-devel] [PATCH v2 6/9] android: camera_device: Add a queue for sending capture results

Laurent Pinchart laurent.pinchart at ideasonboard.com
Mon Sep 13 02:59:31 CEST 2021


Hi Umang,

Thank you for the patch.

On Fri, Sep 10, 2021 at 12:36:35PM +0530, Umang Jain wrote:
> When a camera capture request completes, the next step is to send the
> capture results to the framework via process_capture_results(). All
> the capture associated result metadata is prepared and populated. If
> any post-processing is required, it will also take place in the same
> thread (which can be blocking for a subtaintial amount of time) before
> the results can be sent back to the framework.
> 
> Subsequent patches will move the post-processing to run in a separate
> thread. In order to do so, there is few bits of groundwork which this
> patch entails. Mainly, we need to preserve the order of sending
> the capture results back in which they were queued by the framework
> to the HAL (i.e. the order is sequential). Hence, we need to add a queue
> in which capture results can be queued with context.
> 
> As per this patch, the post-processor still runs synchronously as
> before, but it will queue up the current capture results with context.
> Later down the line, the capture results are dequeud in order and
> sent back to the framework via sendQueuedCaptureResults(). If the queue
> is empty or unused, the current behaviour is to skip the queue and
> send capture results directly.
> 
> The context is preserved using Camera3RequestDescriptor utility
> structure. It has been expanded accordingly to address the needs of
> the functionality.
> ---
> Check if we can drop unique_ptr to camera3descriptor
> ---
> 
> Signed-off-by: Umang Jain <umang.jain at ideasonboard.com>
> ---
>  src/android/camera_device.cpp | 106 ++++++++++++++++++++++++++++++----
>  src/android/camera_device.h   |  22 +++++++
>  src/android/camera_stream.cpp |   2 +
>  3 files changed, 119 insertions(+), 11 deletions(-)
> 
> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> index 84549d04..7f04d225 100644
> --- a/src/android/camera_device.cpp
> +++ b/src/android/camera_device.cpp
> @@ -240,6 +240,8 @@ Camera3RequestDescriptor::Camera3RequestDescriptor(
>  	/* Clone the controls associated with the camera3 request. */
>  	settings_ = CameraMetadata(camera3Request->settings);
>  
> +	internalBuffer_ = nullptr;
> +
>  	/*
>  	 * Create the CaptureRequest, stored as a unique_ptr<> to tie its
>  	 * lifetime to the descriptor.
> @@ -989,6 +991,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
>  			 * once it has been processed.
>  			 */
>  			buffer = cameraStream->getBuffer();
> +			descriptor.internalBuffer_ = buffer;
>  			LOG(HAL, Debug) << ss.str() << " (internal)";
>  			break;
>  		}
> @@ -1148,26 +1151,107 @@ void CameraDevice::requestComplete(Request *request)
>  			continue;
>  		}
>  
> +
> +		/*
> +		 * Save the current context of capture result and queue the
> +		 * descriptor before processing the camera stream.
> +		 *
> +		 * When the processing completes, the descriptor will be
> +		 * dequeued and capture results will be sent to the framework.
> +		 */
> +		descriptor.status_ = Camera3RequestDescriptor::Pending;

The status should also be initialized to Pending in the
Camera3RequestDescriptor constructor.

> +		descriptor.resultMetadata_ = std::move(resultMetadata);

Let's move the resultMetadata to the descriptor right when creating it,
it will be cleaner.

> +		descriptor.captureResult_ = captureResult;

This causes a copy of a fairly large structure. You can prevent this by
replacing

	camera3_capture_result_t captureResult = {};

above with

	camera3_capture_result_t &captureResult = descriptor.captureResult_;
	captureResult = {};

or better, in the definition of Camera3RequestDescriptor, replace

	camera3_capture_result_t captureResult_;

with

	camera3_capture_result_t captureResult_ = {};

and drop the initialization in this function. This will ensure that the
capture results are correctly initialized as soon as
Camera3RequestDescriptor is constructed, avoiding possibly
use-before-init issues.

> +
> +		std::unique_ptr<Camera3RequestDescriptor> reqDescriptor =
> +			std::make_unique<Camera3RequestDescriptor>();
> +		*reqDescriptor = std::move(descriptor);

This will copy the whole class as there's no move constructor for
Camera3RequestDescriptor. I don't think that's what you want. If the
descriptor needs to be moved out of the map to the queue, without a
copy, then they should be stored in the map as unique_ptr already. This
should be done as a separate patch before this one.

I wonder if we shouldn't keep the descriptor in the map until we finish
post-processing, as that would help implementing partial completion
support in the future (where we would connect the bufferComplete signal
and start post-processing of buffers before libcamera completes the
whole request). Maybe that will be best handled in the future, not now,
so I think I'm fine moving descriptors from the map to the queue for
now.

> +		queuedDescriptor_.push_back(std::move(reqDescriptor));
> +
> +		Camera3RequestDescriptor *currentDescriptor = queuedDescriptor_.back().get();
>  		int ret = cameraStream->process(src, *buffer.buffer,

ret is unused, this causes a compilation failure. Could you please
compile-test individual patches in the series ?

Now that camera stream processing completion is signaled, the process()
function should become void. This can go in a separate patch.

> -						descriptor.settings_,
> -						resultMetadata.get(),
> -						&descriptor);
> +						currentDescriptor->settings_,
> +						currentDescriptor->resultMetadata_.get(),

You can drop the resultMetadata parameter from the process() function
and access it through the descriptor instead.

> +						currentDescriptor);
> +		return;
> +	}
> +
> +	if (queuedDescriptor_.empty()) {
> +		captureResult.result = resultMetadata->get();
> +		callbacks_->process_capture_result(callbacks_, &captureResult);
> +	} else {
> +		/*
> +		 * Previous capture results waiting to be sent to framework
> +		 * hence, queue the current capture results as well. After that,
> +		 * check if any results are ready to be sent back to the
> +		 * framework.
> +		 */
> +		descriptor.status_ = Camera3RequestDescriptor::Succeeded;
> +		descriptor.resultMetadata_ = std::move(resultMetadata);
> +		descriptor.captureResult_ = captureResult;
> +		std::unique_ptr<Camera3RequestDescriptor> reqDescriptor =
> +			std::make_unique<Camera3RequestDescriptor>();
> +		*reqDescriptor = std::move(descriptor);
> +		queuedDescriptor_.push_back(std::move(reqDescriptor));
> +
> +		sendQueuedCaptureResults();
> +	}
> +}
> +
> +void CameraDevice::streamProcessingComplete(CameraStream *cameraStream,
> +					    CameraStream::ProcessStatus status,
> +					    const Camera3RequestDescriptor *context)
> +{
> +	for (auto &d : queuedDescriptor_) {

This function is called from the post-processing completion thread, so
it will race with CameraDevice::requestComplete(). Both access
queuedDescriptor_ with a lock, it won't end well.

> +		if (d.get() != context)
> +			continue;

Iterating over a queue to find the element we need sounds like we're not
using the right type of container. I think you should keep the
Camera3RequestDescriptor in the map, and look it up from the map here.

> +
>  		/*
>  		 * Return the FrameBuffer to the CameraStream now that we're
>  		 * done processing it.
>  		 */
>  		if (cameraStream->type() == CameraStream::Type::Internal)
> -			cameraStream->putBuffer(src);
> -
> -		if (ret) {
> -			buffer.status = CAMERA3_BUFFER_STATUS_ERROR;
> -			notifyError(descriptor.frameNumber_, buffer.stream,
> -				    CAMERA3_MSG_ERROR_BUFFER);
> +			cameraStream->putBuffer(d->internalBuffer_);
> +
> +		if (status == CameraStream::ProcessStatus::Succeeded) {
> +			d->status_ = Camera3RequestDescriptor::Succeeded;
> +		} else {
> +			d->status_ = Camera3RequestDescriptor::Failed;
> +
> +			d->captureResult_.partial_result = 0;
> +			for (camera3_stream_buffer_t &buffer : d->buffers_) {
> +				CameraStream *cs = static_cast<CameraStream *>(buffer.stream->priv);
> +
> +				if (cs->camera3Stream().format != HAL_PIXEL_FORMAT_BLOB)
> +					continue;
> +				buffer.status = CAMERA3_BUFFER_STATUS_ERROR;
> +				notifyError(d->frameNumber_, buffer.stream,
> +					    CAMERA3_MSG_ERROR_BUFFER);
> +			}
>  		}
> +		break;
>  	}
>  
> -	captureResult.result = resultMetadata->get();
> -	callbacks_->process_capture_result(callbacks_, &captureResult);
> +	/*
> +	 * Send back capture results to the framework by inspecting the queue.
> +	 * The framework can defer queueing further requests to the HAL (via
> +	 * process_capture_request) unless until it receives the capture results
> +	 * for already queued requests.
> +	 */
> +	sendQueuedCaptureResults();
> +}
> +
> +void CameraDevice::sendQueuedCaptureResults()
> +{
> +	while (!queuedDescriptor_.empty()) {
> +		std::unique_ptr<Camera3RequestDescriptor> &d = queuedDescriptor_.front();
> +		if (d->status_ == Camera3RequestDescriptor::Pending)
> +			return;
> +
> +		d->captureResult_.result = d->resultMetadata_->get();
> +		callbacks_->process_capture_result(callbacks_, &(d->captureResult_));
> +		queuedDescriptor_.pop_front();
> +	}
>  }
>  
>  std::string CameraDevice::logPrefix() const
> diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> index b59ac3e7..e7318358 100644
> --- a/src/android/camera_device.h
> +++ b/src/android/camera_device.h
> @@ -7,6 +7,7 @@
>  #ifndef __ANDROID_CAMERA_DEVICE_H__
>  #define __ANDROID_CAMERA_DEVICE_H__
>  
> +#include <deque>
>  #include <map>
>  #include <memory>
>  #include <mutex>
> @@ -46,6 +47,20 @@ struct Camera3RequestDescriptor {
>  	std::vector<std::unique_ptr<libcamera::FrameBuffer>> frameBuffers_;
>  	CameraMetadata settings_;
>  	std::unique_ptr<CaptureRequest> request_;
> +
> +	/*
> +	 * Below are utility placeholders used when a capture result
> +	 * needs to be queued before completion via process_capture_result()
> +	 */
> +	enum completionStatus {
> +		Pending,
> +		Succeeded,
> +		Failed,
> +	};
> +	std::unique_ptr<CameraMetadata> resultMetadata_;
> +	camera3_capture_result_t captureResult_;

This patch is a bit hard to review. Could you split the addition of
resultMetadata_ and captureResult_ to Camera3RequestDescriptor to a
separate patch (including addressing the comments above) ?

> +	libcamera::FrameBuffer *internalBuffer_;
> +	completionStatus status_;
>  };
>  
>  class CameraDevice : protected libcamera::Loggable
> @@ -78,6 +93,9 @@ public:
>  	int processCaptureRequest(camera3_capture_request_t *request);
>  	void requestComplete(libcamera::Request *request);
>  
> +	void streamProcessingComplete(CameraStream *stream,
> +				      CameraStream::ProcessStatus status,
> +				      const Camera3RequestDescriptor *context);
>  protected:
>  	std::string logPrefix() const override;
>  
> @@ -106,6 +124,8 @@ private:
>  	std::unique_ptr<CameraMetadata> getResultMetadata(
>  		const Camera3RequestDescriptor &descriptor) const;
>  
> +	void sendQueuedCaptureResults();
> +
>  	unsigned int id_;
>  	camera3_device_t camera3Device_;
>  
> @@ -126,6 +146,8 @@ private:
>  	libcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */
>  	std::map<uint64_t, Camera3RequestDescriptor> descriptors_;
>  
> +	std::deque<std::unique_ptr<Camera3RequestDescriptor>> queuedDescriptor_;
> +
>  	std::string maker_;
>  	std::string model_;
>  
> diff --git a/src/android/camera_stream.cpp b/src/android/camera_stream.cpp
> index 996779c4..c7d874b2 100644
> --- a/src/android/camera_stream.cpp
> +++ b/src/android/camera_stream.cpp
> @@ -134,6 +134,8 @@ void CameraStream::postProcessingComplete(PostProcessor::Status status,
>  		processStatus = ProcessStatus::Succeeded;
>  	else
>  		processStatus = ProcessStatus::Failed;
> +
> +	cameraDevice_->streamProcessingComplete(this, processStatus, context);
>  }
>  
>  FrameBuffer *CameraStream::getBuffer()

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list