[libcamera-devel] [PATCH v2 2/3] android: camera_device: Transform descriptors_ map to queue

Umang Jain umang.jain at ideasonboard.com
Wed Sep 29 07:40:08 CEST 2021


Hi Hiro,

On 9/29/21 4:59 AM, Hirokazu Honda wrote:
> Hi Umang, thank you for the patch.
>
> On Wed, Sep 29, 2021 at 6:07 AM Laurent Pinchart
> <laurent.pinchart at ideasonboard.com> wrote:
>> Hi Umang,
>>
>> Thank you for the patch.
>>
>> On Tue, Sep 28, 2021 at 09:55:35PM +0530, Umang Jain wrote:
>>> The descriptors_ map holds Camera3RequestDescriptor(s) which are
>>> per-capture requests placed by the framework to libcamera HAL.
>>> CameraDevice::requestComplete() looks for the descriptor for which the
>>> camera request has been completed and removes it from the map.
>>> Since the requests are placed in form of FIFO and the framework expects
>>> the order of completion to be FIFO as well, this calls for a need of
>>> a queue rather than a std::map.
>>>
>>> This patch still keeps the same lifetime of Camera3RequestDescriptor as
>>> before i.e. in the requestComplete(). Previously, a descriptor was
>>> extracted from the map and its lifetime was bound to requestComplete().
>>> The lifetime is kept same by manually calling .pop_front() on the
>>> queue. In the subsequent commit, this is likely to change with a
>>> centralized location of dropping descriptors from the queue for request
>>> completion.
>>>
>>> Signed-off-by: Umang Jain <umang.jain at ideasonboard.com>
>>> ---
>>>   src/android/camera_device.cpp | 92 +++++++++++++++++++----------------
>>>   src/android/camera_device.h   |  3 +-
>>>   2 files changed, 52 insertions(+), 43 deletions(-)
>>>
>>> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
>>> index 9287ea07..a3b8a549 100644
>>> --- a/src/android/camera_device.cpp
>>> +++ b/src/android/camera_device.cpp
>>> @@ -457,7 +457,9 @@ void CameraDevice::stop()
>>>        worker_.stop();
>>>        camera_->stop();
>>>
>>> -     descriptors_.clear();
>>> +     while (!descriptors_.empty())
>>> +             descriptors_.pop();
>> You can simplify this with
>>
>>          descriptors_ = {};
>>
>>> +
>>>        state_ = State::Stopped;
>>>   }
>>>
>>> @@ -955,7 +957,9 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
>>>         * The descriptor and the associated memory reserved here are freed
>>>         * at request complete time.
>>>         */
>>> -     Camera3RequestDescriptor descriptor(camera_.get(), camera3Request);
>>> +     std::unique_ptr<Camera3RequestDescriptor> descriptor =
>>> +             std::make_unique<Camera3RequestDescriptor>(camera_.get(),
>>> +                                                        camera3Request);
> nit: I would use auto in the type declaration in using
> std::make_unique because the type is obvious from the right formula.
>
>>>        /*
>>>         * \todo The Android request model is incremental, settings passed in
>>> @@ -966,12 +970,12 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
>>>        if (camera3Request->settings)
>>>                lastSettings_ = camera3Request->settings;
>>>        else
>>> -             descriptor.settings_ = lastSettings_;
>>> +             descriptor->settings_ = lastSettings_;
>>> +
>>> +     LOG(HAL, Debug) << "Queueing request " << descriptor->request_->cookie()
>>> +                     << " with " << descriptor->buffers_.size() << " streams";
>>>
>>> -     LOG(HAL, Debug) << "Queueing request " << descriptor.request_->cookie()
>>> -                     << " with " << descriptor.buffers_.size() << " streams";
>>> -     for (unsigned int i = 0; i < descriptor.buffers_.size(); ++i) {
>>> -             const camera3_stream_buffer_t &camera3Buffer = descriptor.buffers_[i];
>>> +     for (const auto &[i, camera3Buffer] : utils::enumerate(descriptor->buffers_)) {
>>>                camera3_stream *camera3Stream = camera3Buffer.stream;
>>>                CameraStream *cameraStream = static_cast<CameraStream *>(camera3Stream->priv);
>>>
>>> @@ -1007,12 +1011,12 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
>>>                         * associate it with the Camera3RequestDescriptor for
>>>                         * lifetime management only.
>>>                         */
>>> -                     descriptor.frameBuffers_.push_back(
>>> +                     descriptor->frameBuffers_.push_back(
>>>                                createFrameBuffer(*camera3Buffer.buffer,
>>>                                                  cameraStream->configuration().pixelFormat,
>>>                                                  cameraStream->configuration().size));
>>>
>>> -                     buffer = descriptor.frameBuffers_.back().get();
>>> +                     buffer = descriptor->frameBuffers_.back().get();
>>>                        acquireFence = camera3Buffer.acquire_fence;
>>>                        LOG(HAL, Debug) << ss.str() << " (direct)";
>>>                        break;
>>> @@ -1035,7 +1039,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
>>>                        return -ENOMEM;
>>>                }
>>>
>>> -             descriptor.request_->addBuffer(cameraStream->stream(), buffer,
>>> +             descriptor->request_->addBuffer(cameraStream->stream(), buffer,
>>>                                               acquireFence);
>>>        }
>>>
>>> @@ -1043,7 +1047,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
>>>         * Translate controls from Android to libcamera and queue the request
>>>         * to the CameraWorker thread.
>>>         */
>>> -     int ret = processControls(&descriptor);
>>> +     int ret = processControls(descriptor.get());
>>>        if (ret)
>>>                return ret;
>>>
>>> @@ -1071,11 +1075,11 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
>>>                state_ = State::Running;
>>>        }
>>>
>>> -     CaptureRequest *request = descriptor.request_.get();
>>> +     CaptureRequest *request = descriptor->request_.get();
>>>
>>>        {
>>>                MutexLocker descriptorsLock(descriptorsMutex_);
>>> -             descriptors_[descriptor.request_->cookie()] = std::move(descriptor);
>>> +             descriptors_.push(std::move(descriptor));
>>>        }
>>>
>>>        worker_.queueRequest(request);
>>> @@ -1085,26 +1089,26 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
>>>
>>>   void CameraDevice::requestComplete(Request *request)
>>>   {
>>> -     decltype(descriptors_)::node_type node;
>>> +     Camera3RequestDescriptor *descriptor;
>>>        {
>>>                MutexLocker descriptorsLock(descriptorsMutex_);
>>> -             auto it = descriptors_.find(request->cookie());
>>> -             if (it == descriptors_.end()) {
>>> -                     /*
>>> -                      * \todo Clarify if the Camera has to be closed on
>>> -                      * ERROR_DEVICE and possibly demote the Fatal to simple
>>> -                      * Error.
>>> -                      */
>>> -                     notifyError(0, nullptr, CAMERA3_MSG_ERROR_DEVICE);
>>> -                     LOG(HAL, Fatal)
>>> -                             << "Unknown request: " << request->cookie();
>>> +             ASSERT(!descriptors_.empty());
>>> +             descriptor = descriptors_.front().get();
> Why isn't descriptor popped here?
> Is there any case that a descriptor should be kept?
> The original code removes the descriptor from descriptors_.
> This changes the logic.


A bit yes, but it does respect the lifetime of the descriptor as before 
(as the std::map)

Provided that this is a transient patch that works on descriptors_, I 
prefer to be a little flexible here with logic. The popping of 
descriptors_ has been moved to a centralized location in 3/3

> I also think removing here is safe because although a pointer of node
> in std::queue() is not invalidated by push operation, but indeed can
> be invalidated by pop operation.
> So removing here (while acquiring a lock) avoids the problem of using
> an invalid pointer in the case descriptors_ are cleared in parallel.


Yes there might be cases in the future where this can happen, I can 
think of some stop() or flush() clearing the descriptors_, but we are 
going to address them step by step.

The clearing of descriptors_ is something that shouldn't be happening by 
design, so we need to handle it as case-by-case basis (which is one the 
goals we intend to pursue as part of this HAL rework), in near-future 
patch series.


>
> -Hiro
>>> +     }
>>>
>>> -                     return;
>>> -             }
>>> +     if (descriptor->request_->cookie() != request->cookie()) {
>>> +             /*
>>> +              * \todo Clarify if the Camera has to be closed on
>>> +              * ERROR_DEVICE and possibly demote the Fatal to simple
>>> +              * Error.
>>> +              */
>>> +             notifyError(0, nullptr, CAMERA3_MSG_ERROR_DEVICE);
>>> +             LOG(HAL, Fatal)
>>> +                     << "Out-of-order completion for request: "
>> s/://
>>
>> Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
>>
>>> +                     << utils::hex(request->cookie());
>>>
>>> -             node = descriptors_.extract(it);
>>> +             return;
>>>        }
>>> -     Camera3RequestDescriptor &descriptor = node.mapped();
>>>
>>>        /*
>>>         * Prepare the capture result for the Android camera stack.
>>> @@ -1113,9 +1117,9 @@ void CameraDevice::requestComplete(Request *request)
>>>         * post-processing/compression fails.
>>>         */
>>>        camera3_capture_result_t captureResult = {};
>>> -     captureResult.frame_number = descriptor.frameNumber_;
>>> -     captureResult.num_output_buffers = descriptor.buffers_.size();
>>> -     for (camera3_stream_buffer_t &buffer : descriptor.buffers_) {
>>> +     captureResult.frame_number = descriptor->frameNumber_;
>>> +     captureResult.num_output_buffers = descriptor->buffers_.size();
>>> +     for (camera3_stream_buffer_t &buffer : descriptor->buffers_) {
>>>                CameraStream *cameraStream =
>>>                        static_cast<CameraStream *>(buffer.stream->priv);
>>>
>>> @@ -1135,7 +1139,7 @@ void CameraDevice::requestComplete(Request *request)
>>>                buffer.release_fence = -1;
>>>                buffer.status = CAMERA3_BUFFER_STATUS_OK;
>>>        }
>>> -     captureResult.output_buffers = descriptor.buffers_.data();
>>> +     captureResult.output_buffers = descriptor->buffers_.data();
>>>        captureResult.partial_result = 1;
>>>
>>>        /*
>>> @@ -1147,11 +1151,11 @@ void CameraDevice::requestComplete(Request *request)
>>>                                << " not successfully completed: "
>>>                                << request->status();
>>>
>>> -             notifyError(descriptor.frameNumber_, nullptr,
>>> +             notifyError(descriptor->frameNumber_, nullptr,
>>>                            CAMERA3_MSG_ERROR_REQUEST);
>>>
>>>                captureResult.partial_result = 0;
>>> -             for (camera3_stream_buffer_t &buffer : descriptor.buffers_) {
>>> +             for (camera3_stream_buffer_t &buffer : descriptor->buffers_) {
>>>                        /*
>>>                         * Signal to the framework it has to handle fences that
>>>                         * have not been waited on by setting the release fence
>>> @@ -1164,6 +1168,7 @@ void CameraDevice::requestComplete(Request *request)
>>>
>>>                callbacks_->process_capture_result(callbacks_, &captureResult);
>>>
> A lock is not acquired here?
> MutexLocker descriptorsLock(descriptorsMutex_);


ops, yeah, I'll fix it!

>
>>> +             descriptors_.pop();
>>>                return;
>>>        }
>>>
>>> @@ -1175,10 +1180,10 @@ void CameraDevice::requestComplete(Request *request)
>>>         */
>>>        uint64_t sensorTimestamp = static_cast<uint64_t>(request->metadata()
>>>                                                         .get(controls::SensorTimestamp));
>>> -     notifyShutter(descriptor.frameNumber_, sensorTimestamp);
>>> +     notifyShutter(descriptor->frameNumber_, sensorTimestamp);
>>>
>>>        LOG(HAL, Debug) << "Request " << request->cookie() << " completed with "
>>> -                     << descriptor.buffers_.size() << " streams";
>>> +                     << descriptor->buffers_.size() << " streams";
>>>
>>>        /*
>>>         * Generate the metadata associated with the captured buffers.
>>> @@ -1186,16 +1191,16 @@ void CameraDevice::requestComplete(Request *request)
>>>         * Notify if the metadata generation has failed, but continue processing
>>>         * buffers and return an empty metadata pack.
>>>         */
>>> -     std::unique_ptr<CameraMetadata> resultMetadata = getResultMetadata(descriptor);
>>> +     std::unique_ptr<CameraMetadata> resultMetadata = getResultMetadata(*descriptor);
>>>        if (!resultMetadata) {
>>> -             notifyError(descriptor.frameNumber_, nullptr, CAMERA3_MSG_ERROR_RESULT);
>>> +             notifyError(descriptor->frameNumber_, nullptr, CAMERA3_MSG_ERROR_RESULT);
>>>
>>>                /* The camera framework expects an empty metadata pack on error. */
>>>                resultMetadata = std::make_unique<CameraMetadata>(0, 0);
>>>        }
>>>
>>>        /* Handle post-processing. */
>>> -     for (camera3_stream_buffer_t &buffer : descriptor.buffers_) {
>>> +     for (camera3_stream_buffer_t &buffer : descriptor->buffers_) {
>>>                CameraStream *cameraStream =
>>>                        static_cast<CameraStream *>(buffer.stream->priv);
>>>
>>> @@ -1206,13 +1211,13 @@ void CameraDevice::requestComplete(Request *request)
>>>                if (!src) {
>>>                        LOG(HAL, Error) << "Failed to find a source stream buffer";
>>>                        buffer.status = CAMERA3_BUFFER_STATUS_ERROR;
>>> -                     notifyError(descriptor.frameNumber_, buffer.stream,
>>> +                     notifyError(descriptor->frameNumber_, buffer.stream,
>>>                                    CAMERA3_MSG_ERROR_BUFFER);
>>>                        continue;
>>>                }
>>>
>>>                int ret = cameraStream->process(*src, buffer,
>>> -                                             descriptor.settings_,
>>> +                                             descriptor->settings_,
>>>                                                resultMetadata.get());
>>>                /*
>>>                 * Return the FrameBuffer to the CameraStream now that we're
>>> @@ -1223,13 +1228,16 @@ void CameraDevice::requestComplete(Request *request)
>>>
>>>                if (ret) {
>>>                        buffer.status = CAMERA3_BUFFER_STATUS_ERROR;
>>> -                     notifyError(descriptor.frameNumber_, buffer.stream,
>>> +                     notifyError(descriptor->frameNumber_, buffer.stream,
>>>                                    CAMERA3_MSG_ERROR_BUFFER);
>>>                }
>>>        }
>>>
>>>        captureResult.result = resultMetadata->get();
>>>        callbacks_->process_capture_result(callbacks_, &captureResult);
>>> +
>>> +     MutexLocker descriptorsLock(descriptorsMutex_);
>>> +     descriptors_.pop();
>>>   }
>>>
>>>   std::string CameraDevice::logPrefix() const
>>> diff --git a/src/android/camera_device.h b/src/android/camera_device.h
>>> index 43eb5895..9ec510d5 100644
>>> --- a/src/android/camera_device.h
>>> +++ b/src/android/camera_device.h
>>> @@ -10,6 +10,7 @@
>>>   #include <map>
>>>   #include <memory>
>>>   #include <mutex>
>>> +#include <queue>
>>>   #include <vector>
>>>
>>>   #include <hardware/camera3.h>
>>> @@ -125,7 +126,7 @@ private:
>>>        std::vector<CameraStream> streams_;
>>>
>>>        libcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */
>>> -     std::map<uint64_t, Camera3RequestDescriptor> descriptors_;
>>> +     std::queue<std::unique_ptr<Camera3RequestDescriptor>> descriptors_;
>>>
>>>        std::string maker_;
>>>        std::string model_;
>> --
>> Regards,
>>
>> Laurent Pinchart


More information about the libcamera-devel mailing list