[libcamera-devel] [PATCH v2 8/9] android: camera_stream: Run post-processor in a separate thread

Umang Jain umang.jain at ideasonboard.com
Mon Sep 13 09:09:11 CEST 2021


Hi Laurent,

On 9/13/21 6:51 AM, Laurent Pinchart wrote:
> Hi Umang,
>
> Thank you for the patch.
>
> On Fri, Sep 10, 2021 at 12:36:37PM +0530, Umang Jain wrote:
>> In CameraStream, introduce a new private PostProcessorWorker
>> class derived from Object. A instance of PostProcessorWorker
>> is moved to a separate thread instance which will be responsible
>> to run the post-processor.
>>
>> Running PostProcessor asynchronously should entail that all the
>> data context needed by the PostProcessor should remain valid for
>> the entire duration of its run. Most of the context preserving
>> part has been addressed in the previous commits, we just need to
>> ensure the source framebuffer data that comes via Camera::Request,
>> should remain valid for the entire duration of post-processing
>> running asynchronously. In order to so, we maintain a separate
>> copy of the framebuffer data and add it to the Camera3RequestDescriptor
>> structure in which we preserve rest of the context.
>>
>> Signed-off-by: Umang Jain <umang.jain at ideasonboard.com>
>> ---
>>   src/android/camera_device.cpp | 19 ++++++++++++++++++-
>>   src/android/camera_device.h   |  4 ++++
>>   src/android/camera_stream.cpp | 16 ++++++++++++++--
>>   src/android/camera_stream.h   | 28 ++++++++++++++++++++++++++++
>>   4 files changed, 64 insertions(+), 3 deletions(-)
>>
>> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
>> index 988d4232..73eb5758 100644
>> --- a/src/android/camera_device.cpp
>> +++ b/src/android/camera_device.cpp
>> @@ -1174,13 +1174,29 @@ void CameraDevice::requestComplete(Request *request)
>>   			return;
>>   		}
>>   
>> +		FrameBuffer *source = src;
>> +		if (cameraStream->type() != CameraStream::Type::Internal) {
>> +			/*
>> +			 * The source buffer is owned by Request object which
>> +			 * can be reused by libcamera. Since post-processor will
>> +			 * run asynchrnously, we need to copy the request's
>> +			 * frame buffer and use that as the source buffer for
>> +			 * post processing.
> I don't think this is right. The request can be reused indeed, but with
> new FrameBuffer instances every time. The frame buffers are owned by
> Camera3RequestDescriptor, they're stored in a vector there.


Yes, I realized by discussion with Jacopo. I think we don't need a copy 
here, working on it as we speak.

>
>> +			 */
>> +			for (const auto &plane : src->planes())
>> +				descriptor.srcPlanes_.push_back(plane);
>> +			descriptor.srcFramebuffer_ =
>> +				std::make_unique<FrameBuffer>(descriptor.srcPlanes_);
>> +			source = descriptor.srcFramebuffer_.get();
>> +		}
>> +
>>   		std::unique_ptr<Camera3RequestDescriptor> reqDescriptor =
>>   			std::make_unique<Camera3RequestDescriptor>();
>>   		*reqDescriptor = std::move(descriptor);
>>   		queuedDescriptor_.push_back(std::move(reqDescriptor));
>>   
>>   		Camera3RequestDescriptor *currentDescriptor = queuedDescriptor_.back().get();
>> -		int ret = cameraStream->process(src,
>> +		int ret = cameraStream->process(source,
>>   						currentDescriptor->destBuffer_.get(),
>>   						currentDescriptor->settings_,
>>   						currentDescriptor->resultMetadata_.get(),
>> @@ -1255,6 +1271,7 @@ void CameraDevice::streamProcessingComplete(CameraStream *cameraStream,
>>   
>>   void CameraDevice::sendQueuedCaptureResults()
>>   {
>> +	MutexLocker lock(queuedDescriptorsMutex_);
>>   	while (!queuedDescriptor_.empty()) {
>>   		std::unique_ptr<Camera3RequestDescriptor> &d = queuedDescriptor_.front();
>>   		if (d->status_ == Camera3RequestDescriptor::Pending)
>> diff --git a/src/android/camera_device.h b/src/android/camera_device.h
>> index b62d373c..ecdda06e 100644
>> --- a/src/android/camera_device.h
>> +++ b/src/android/camera_device.h
>> @@ -62,6 +62,9 @@ struct Camera3RequestDescriptor {
>>   	camera3_capture_result_t captureResult_;
>>   	libcamera::FrameBuffer *internalBuffer_;
>>   	completionStatus status_;
>> +
>> +	std::unique_ptr<libcamera::FrameBuffer> srcFramebuffer_;
>> +	std::vector<libcamera::FrameBuffer::Plane> srcPlanes_;
>>   };
>>   
>>   class CameraDevice : protected libcamera::Loggable
>> @@ -147,6 +150,7 @@ private:
>>   	libcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */
>>   	std::map<uint64_t, Camera3RequestDescriptor> descriptors_;
>>   
>> +	libcamera::Mutex queuedDescriptorsMutex_; /* Protects queuedDescriptor_. */
> This belongs to the previous patch.


No, I think as this patch is the one which introduces the thread, it 
should introduce the necessary locking too. Correct me if I am wrong.

>
>>   	std::deque<std::unique_ptr<Camera3RequestDescriptor>> queuedDescriptor_;
>>   
>>   	std::string maker_;
>> diff --git a/src/android/camera_stream.cpp b/src/android/camera_stream.cpp
>> index 5fd04bbf..845e2462 100644
>> --- a/src/android/camera_stream.cpp
>> +++ b/src/android/camera_stream.cpp
>> @@ -55,6 +55,15 @@ CameraStream::CameraStream(CameraDevice *const cameraDevice,
>>   		 * is what we instantiate here.
>>   		 */
>>   		postProcessor_ = std::make_unique<PostProcessorJpeg>(cameraDevice_);
>> +		ppWorker_ = std::make_unique<PostProcessorWorker>(postProcessor_.get());
>> +
>> +		thread_ = std::make_unique<libcamera::Thread>();
>> +		ppWorker_->moveToThread(thread_.get());
>> +		/*
>> +		 * \todo: Class is MoveConstructible, so where to stop thread
>> +		 * if we don't user-defined destructor? See RFC patch at the end.
>> +		 */
>> +		thread_->start();
>>   	}
>>   
>>   	if (type == Type::Internal) {
>> @@ -110,8 +119,11 @@ int CameraStream::process(const FrameBuffer *source,
>>   	if (!postProcessor_)
>>   		return 0;
>>   
>> -	return postProcessor_->process(source, destBuffer, requestMetadata,
>> -				       resultMetadata, context);
>> +	ppWorker_->invokeMethod(&PostProcessorWorker::process,
>> +				ConnectionTypeQueued, source, destBuffer,
>> +				requestMetadata, resultMetadata, context);
>> +
>> +	return 0;
>>   }
>>   
>>   void CameraStream::postProcessingComplete(PostProcessor::Status status,
>> diff --git a/src/android/camera_stream.h b/src/android/camera_stream.h
>> index 8097ddbc..dbb7eee3 100644
>> --- a/src/android/camera_stream.h
>> +++ b/src/android/camera_stream.h
>> @@ -13,7 +13,9 @@
>>   
>>   #include <hardware/camera3.h>
>>   
>> +#include <libcamera/base/object.h>
>>   #include <libcamera/base/signal.h>
>> +#include <libcamera/base/thread.h>
>>   
>>   #include <libcamera/camera.h>
>>   #include <libcamera/framebuffer.h>
>> @@ -23,6 +25,7 @@
>>   
>>   #include "post_processor.h"
>>   
>> +class CameraBuffer;
>>   class CameraDevice;
>>   class CameraMetadata;
>>   
>> @@ -137,6 +140,29 @@ public:
>>   	};
>>   
>>   private:
>> +	class PostProcessorWorker : public libcamera::Object
>> +	{
>> +	public:
>> +		PostProcessorWorker(PostProcessor *postProcessor)
>> +		{
>> +			postProcessor_ = postProcessor;
>> +		}
>> +
>> +		void process(const libcamera::FrameBuffer *source,
>> +			     CameraBuffer *destination,
>> +			     const CameraMetadata &requestMetadata,
>> +			     CameraMetadata *resultMetadata,
>> +			     const Camera3RequestDescriptor *context)
>> +		{
>> +			postProcessor_->process(source, destination,
>> +						requestMetadata, resultMetadata,
>> +						context);
>> +		}
>> +
>> +	private:
>> +		PostProcessor *postProcessor_;
>> +	};
> If it wasn't for the fact that I mentioned in the review of v1 that it
> would be best to keep the PostProcessor class not thread-aware, I'd be
> tempted to say we could make PostProcessor inherit from Object and drop
> this class completely :-) Would you be tempted to do so too ? Compared


I was tempted that from the start which was RFC v1. I should start to 
trust my gut more....

> to v1 (the RFC) it would resurect patch 1/5, but we wouldn't need the
> PostProcessorWorker class at all.


hmmm....

>
> On the other hand, given that I've mentioned in the review of a 7/9 that
> mapping the destination buffer could be done in the post-processor
> thread, maybe PostProcessorWorker::process() is the right place to do
> so. And now that I've written that, I now realize we've split the
> construction of CameraBuffer from the actual mapping, which is performed
> on the first call to CameraBuffer::plane(), so the mapping is already
> done in the post-processor thread. It's thus likely fine to have the
> construction of CameraBuffer in CameraStream or CameraDevice.


Aha, very interesting. So I think this brings one more \todo down in the 
code base

>
>> +
>>   	void postProcessingComplete(PostProcessor::Status status,
>>   				    const Camera3RequestDescriptor *context);
>>   
>> @@ -154,6 +180,8 @@ private:
>>   	 */
>>   	std::unique_ptr<std::mutex> mutex_;
>>   	std::unique_ptr<PostProcessor> postProcessor_;
>> +	std::unique_ptr<PostProcessorWorker> ppWorker_;
>> +	std::unique_ptr<libcamera::Thread> thread_;
>>   };
>>   
>>   #endif /* __ANDROID_CAMERA_STREAM__ */


More information about the libcamera-devel mailing list