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

Laurent Pinchart laurent.pinchart at ideasonboard.com
Wed Sep 8 15:33:26 CEST 2021


On Wed, Sep 08, 2021 at 02:00:41PM +0100, Kieran Bingham wrote:
> On 07/09/2021 20:57, 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
> 
> s/subtaintial/substantial/
> 
> > the results can be sent back to the framework.
> > 
> > A follow up commit 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
> 
> s/dequeud/dequeued/
> 
> > sent back to the framework.
> > 
> > The context is preserved using Camera3RequestDescriptor utility
> > structure. It has been expanded accordingly to address the needs of
> > the functionality.
> > 
> > Signed-off-by: Umang Jain <umang.jain at ideasonboard.com>
> > ---
> >  src/android/camera_device.cpp | 113 +++++++++++++++++++++++++++++++---
> >  src/android/camera_device.h   |  23 +++++++
> >  2 files changed, 126 insertions(+), 10 deletions(-)
> > 
> > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > index f2f36f32..9d4ec02e 100644
> > --- a/src/android/camera_device.cpp
> > +++ b/src/android/camera_device.cpp
> > @@ -240,6 +240,8 @@ CameraDevice::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,25 +1151,115 @@ 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::NOT_FINISHED;
> > +		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));
> > +		Camera3RequestDescriptor *currentDescriptor = queuedDescriptor_.back().get();
> > +
> > +		CameraMetadata *metadata = currentDescriptor->resultMetadata_.get();
> > +		cameraStream->processComplete.connect(
> > +			this, [=](CameraStream::ProcessStatus status) {
> > +				streamProcessingComplete(cameraStream, status,
> > +							 metadata);
> 
> I believe this to be unsafe to manage multiple operations.
> 
> cameraStream->processComplete() should not be connected with per-run
> parameters. Those should be passed as the context of the signal emit(),
> not by the connection.
> 
> Otherwise, the lamba will be overwritten by the next requestComplete()
> and incorrect parameters will be processed when the signal actually fires.

It's worse than that, ever connect() will create a new connection, so
the slot will be called once for the first request, twice for the
second, ...

> > +			});
> > +
> >  		int ret = cameraStream->process(src, *buffer.buffer,
> > -						descriptor.settings_,
> > -						resultMetadata.get());
> > +						currentDescriptor->settings_,
> > +						metadata);
> > +		return;
> > +	}
> > +
> > +	if (queuedDescriptor_.empty()) {
> > +		captureResult.result = resultMetadata->get();
> > +		callbacks_->process_capture_result(callbacks_, &captureResult);
> 
> *1 duplication of completion callbacks (see below)
> 
> > +	} 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::FINISHED_SUCCESS;
> > +		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,
> > +					    CameraMetadata *resultMetadata)
> > +{
> > +	for (auto &d : queuedDescriptor_) {
> > +		if (d->resultMetadata_.get() != resultMetadata)
> > +			continue;
> > +
> >  		/*
> >  		 * 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::Success) {
> > +			d->status_ = Camera3RequestDescriptor::FINISHED_SUCCESS;
> > +		} else {
> > +			d->status_ = Camera3RequestDescriptor::FINISHED_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();
> 
> I'd have this exit early if the front descriptor is not finished.
> 
> 
> > +		if (d->status_ != Camera3RequestDescriptor::NOT_FINISHED) {
> > +			d->captureResult_.result = d->resultMetadata_->get();
> > +			callbacks_->process_capture_result(callbacks_,
> > +							   &(d->captureResult_));
> 
> Not as part of this patch, but I feel like Camera3RequestDescriptor
> could have a method called complete() to wrap the management of the
> completion against a request descriptor.
> 
> There's lots of occasions where the metadata is obtained or processed
> into a result structure and called back with a flag.
> 
> (such as *1 above, but I think there are more)
> 
> It seems to me like that could be made into
> 
> 		d->complete(CameraRequest::Success);
> 
> or something.... But that's an overall design of breaking out the
> Camera3RequestDescriptor to a full class.
> 
> As this series is extending Camera3RequestDescriptor to maintain extra
> state required for the asynchronous post-processing, that makes me think
> it would be better to have a full class to manage that state. (and thus
> methods to operate on it)·
> 
> 
> 
> > +			queuedDescriptor_.pop_front();
> > +		} else {
> > +			break;
> > +		}
> > +	}
> >  }
> >  
> >  std::string CameraDevice::logPrefix() const
> > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > index a5576927..36425773 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>
> > @@ -83,6 +84,21 @@ private:
> >  		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() callback.
> > +		 */
> > +		enum completionStatus {
> > +			NOT_FINISHED,
> 
> I'd call this "Active" or "Pending" to reduce the negation...
> 
> 	"If (state == Active)"
> rather than
> 	"if (state != NOT_FINISHED)"
> 
> 
> > +			FINISHED_SUCCESS,
> 
> I'd call this Success or Succeeded, or Successful ..
> 
> > +			FINISHED_FAILED,
> 
> And Failed
> 
> 
> 
> > +		};
> > +		std::unique_ptr<CameraMetadata> resultMetadata_;
> > +		camera3_capture_result_t captureResult_;
> > +		libcamera::FrameBuffer *internalBuffer_;
> > +		completionStatus status_;
> 
> These are all used to track the post-processor context.
> 
> 
> 
> 
> >  	};
> >  
> >  	enum class State {
> > @@ -105,6 +121,11 @@ private:
> >  	std::unique_ptr<CameraMetadata> getResultMetadata(
> >  		const Camera3RequestDescriptor &descriptor) const;
> >  
> > +	void sendQueuedCaptureResults();
> > +	void streamProcessingComplete(CameraStream *stream,
> > +				      CameraStream::ProcessStatus status,
> > +				      CameraMetadata *resultMetadata);
> > +
> >  	unsigned int id_;
> >  	camera3_device_t camera3Device_;
> >  
> > @@ -125,6 +146,8 @@ private:
> >  	libcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */
> >  	std::map<uint64_t, Camera3RequestDescriptor> descriptors_;
> >  
> > +	std::deque<std::unique_ptr<Camera3RequestDescriptor>> queuedDescriptor_;
> 
> I'm not yet sure if this should be a unique_ptr ..
> 
> 
> > +
> >  	std::string maker_;
> >  	std::string model_;
> >  
> > 

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list