From patchwork Fri May 21 15:42:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12352 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 7E30AC31FF for ; Fri, 21 May 2021 15:41:50 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 18F6D68928; Fri, 21 May 2021 17:41:50 +0200 (CEST) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EF8896891B for ; Fri, 21 May 2021 17:41:46 +0200 (CEST) Received: (Authenticated sender: jacopo@jmondi.org) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id 481241BF20A; Fri, 21 May 2021 15:41:46 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 21 May 2021 17:42:20 +0200 Message-Id: <20210521154227.60186-2-jacopo@jmondi.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210521154227.60186-1-jacopo@jmondi.org> References: <20210521154227.60186-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 1/8] android: Rework request completion notification X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The current implementation of CameraDevice::requestComplete() which handles event notification and calls the framework capture result callback does not handle error notification precisely enough. In detail: - Error notification is an asynchronous callback that has to be notified to the framework as soon as an error condition is detected, and it independent from the process_capture_result() callback - Error notification requires the HAL to report the precise error cause, by specifying the correct CAMERA3_MSG_ERROR_* error code. The current implementation only notifies errors of type CAMERA3_MSG_ERROR_REQUEST at the end of the procedure, before the callback invocation. Rework the procedure to: - Notify CAMERA3_MSG_ERROR_DEVICE and perform library tear-down in case a Fatal error is detected - Notify CAMERA3_MSG_ERROR_REQUEST if the libcamera::Request::status is different than RequestCompleted and immediately call process_capture_result() with all buffers in error state. - Notify the shutter event as soon as possible - Notify CAMERA3_MSG_ERROR_RESULT in case the metadata cannot be generated correctly and call process_capture_result() with the right buffer state regardless of metadata availability. - Notify CAMERA3_MSG_ERROR_BUFFER for buffers whose post-processing failed While at it, return the CameraStream buffer by calling cameraStream->putBuffer() regardless of the post-processing result. No regression detected when running CTS in LIMITED mode. Signed-off-by: Jacopo Mondi Reviewed-by: Hirokazu Honda Reviewed-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/android/camera_device.cpp | 138 +++++++++++++++++++++------------- src/android/camera_device.h | 3 +- 2 files changed, 86 insertions(+), 55 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index b32e8be59134..bd96b355ea92 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -1956,17 +1956,20 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques void CameraDevice::requestComplete(Request *request) { - camera3_buffer_status status = CAMERA3_BUFFER_STATUS_OK; - std::unique_ptr resultMetadata; - decltype(descriptors_)::node_type node; { std::scoped_lock lock(mutex_); 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(); - status = CAMERA3_BUFFER_STATUS_ERROR; + return; } @@ -1974,16 +1977,72 @@ void CameraDevice::requestComplete(Request *request) } Camera3RequestDescriptor &descriptor = node.mapped(); + /* + * Prepare the capture result for the Android camera stack. + * + * The buffer status is set to OK and later changed to ERROR if + * 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_) { + buffer.acquire_fence = -1; + buffer.release_fence = -1; + buffer.status = CAMERA3_BUFFER_STATUS_OK; + } + captureResult.output_buffers = descriptor.buffers_.data(); + captureResult.partial_result = 1; + + /* + * If the Request has failed, abort the request by notifying the error + * and complete the request with all buffers in error state. + */ if (request->status() != Request::RequestComplete) { - LOG(HAL, Error) << "Request not successfully completed: " + LOG(HAL, Error) << "Request " << request->cookie() + << " not successfully completed: " << request->status(); - status = CAMERA3_BUFFER_STATUS_ERROR; + + notifyError(descriptor.frameNumber_, + descriptor.buffers_[0].stream, + CAMERA3_MSG_ERROR_REQUEST); + + captureResult.partial_result = 0; + for (camera3_stream_buffer_t &buffer : descriptor.buffers_) + buffer.status = CAMERA3_BUFFER_STATUS_ERROR; + callbacks_->process_capture_result(callbacks_, &captureResult); + + return; } + /* + * Notify shutter as soon as we have verified we have a valid request. + * + * \todo The shutter event notification should be sent to the framework + * as soon as possible, earlier than request completion time. + */ + uint64_t sensorTimestamp = static_cast(request->metadata() + .get(controls::SensorTimestamp)); + notifyShutter(descriptor.frameNumber_, sensorTimestamp); + LOG(HAL, Debug) << "Request " << request->cookie() << " completed with " << descriptor.buffers_.size() << " streams"; - resultMetadata = getResultMetadata(descriptor); + /* + * Generate the metadata associated with the captured buffers. + * + * Notify if the metadata generation has failed, but continue processing + * buffers and return an empty metadata pack. + */ + std::unique_ptr resultMetadata = getResultMetadata(descriptor); + if (!resultMetadata) { + notifyError(descriptor.frameNumber_, descriptor.buffers_[0].stream, + CAMERA3_MSG_ERROR_RESULT); + + /* The camera framework expects an empy metadata pack on error. */ + resultMetadata = std::make_unique(0, 0); + } + captureResult.result = resultMetadata->get(); /* Handle any JPEG compression. */ for (camera3_stream_buffer_t &buffer : descriptor.buffers_) { @@ -1996,56 +2055,27 @@ void CameraDevice::requestComplete(Request *request) FrameBuffer *src = request->findBuffer(cameraStream->stream()); if (!src) { LOG(HAL, Error) << "Failed to find a source stream buffer"; + buffer.status = CAMERA3_BUFFER_STATUS_ERROR; + notifyError(descriptor.frameNumber_, buffer.stream, + CAMERA3_MSG_ERROR_BUFFER); continue; } - int ret = cameraStream->process(*src, - *buffer.buffer, + int ret = cameraStream->process(*src, *buffer.buffer, descriptor.settings_, resultMetadata.get()); - if (ret) { - status = CAMERA3_BUFFER_STATUS_ERROR; - continue; - } - /* * Return the FrameBuffer to the CameraStream now that we're * done processing it. */ if (cameraStream->type() == CameraStream::Type::Internal) cameraStream->putBuffer(src); - } - /* Prepare to call back the Android camera stack. */ - 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_) { - buffer.acquire_fence = -1; - buffer.release_fence = -1; - buffer.status = status; - } - captureResult.output_buffers = descriptor.buffers_.data(); - - if (status == CAMERA3_BUFFER_STATUS_OK) { - uint64_t timestamp = - static_cast(request->metadata() - .get(controls::SensorTimestamp)); - notifyShutter(descriptor.frameNumber_, timestamp); - - captureResult.partial_result = 1; - captureResult.result = resultMetadata->get(); - } - - if (status == CAMERA3_BUFFER_STATUS_ERROR || !captureResult.result) { - /* \todo Improve error handling. In case we notify an error - * because the metadata generation fails, a shutter event has - * already been notified for this frame number before the error - * is here signalled. Make sure the error path plays well with - * the camera stack state machine. - */ - notifyError(descriptor.frameNumber_, - descriptor.buffers_[0].stream); + if (ret) { + buffer.status = CAMERA3_BUFFER_STATUS_ERROR; + notifyError(descriptor.frameNumber_, buffer.stream, + CAMERA3_MSG_ERROR_BUFFER); + } } callbacks_->process_capture_result(callbacks_, &captureResult); @@ -2067,23 +2097,23 @@ void CameraDevice::notifyShutter(uint32_t frameNumber, uint64_t timestamp) callbacks_->notify(callbacks_, ¬ify); } -void CameraDevice::notifyError(uint32_t frameNumber, camera3_stream_t *stream) +void CameraDevice::notifyError(uint32_t frameNumber, camera3_stream_t *stream, + camera3_error_msg_code code) { camera3_notify_msg_t notify = {}; - /* - * \todo Report and identify the stream number or configuration to - * clarify the stream that failed. - */ - LOG(HAL, Error) << "Error occurred on frame " << frameNumber << " (" - << toPixelFormat(stream->format).toString() << ")"; - notify.type = CAMERA3_MSG_ERROR; notify.message.error.error_stream = stream; notify.message.error.frame_number = frameNumber; - notify.message.error.error_code = CAMERA3_MSG_ERROR_REQUEST; + notify.message.error.error_code = code; callbacks_->notify(callbacks_, ¬ify); + + if (stream) + LOG(HAL, Error) << "Error occurred on frame " << frameNumber << " (" + << toPixelFormat(stream->format).toString() << ")"; + else + LOG(HAL, Error) << "Fatal error occurred on device"; } /* diff --git a/src/android/camera_device.h b/src/android/camera_device.h index ae162a452499..a34e8a2cd900 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -100,7 +100,8 @@ private: libcamera::FrameBuffer *createFrameBuffer(const buffer_handle_t camera3buffer); void notifyShutter(uint32_t frameNumber, uint64_t timestamp); - void notifyError(uint32_t frameNumber, camera3_stream_t *stream); + void notifyError(uint32_t frameNumber, camera3_stream_t *stream, + camera3_error_msg_code code); std::unique_ptr requestTemplatePreview(); std::unique_ptr requestTemplateVideo(); libcamera::PixelFormat toPixelFormat(int format) const; From patchwork Fri May 21 15:42:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12353 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id E5E17C31FF for ; Fri, 21 May 2021 15:41:51 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C7E2068925; Fri, 21 May 2021 17:41:50 +0200 (CEST) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 07A7B6891A for ; Fri, 21 May 2021 17:41:47 +0200 (CEST) Received: (Authenticated sender: jacopo@jmondi.org) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id 27B441BF212; Fri, 21 May 2021 15:41:46 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 21 May 2021 17:42:21 +0200 Message-Id: <20210521154227.60186-3-jacopo@jmondi.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210521154227.60186-1-jacopo@jmondi.org> References: <20210521154227.60186-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 2/8] libcamera: request: Add Request::cancel() X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a cancel() function to the Request class that allows to forcefully complete the request and its associated buffers in error state. Only pending requests can be forcefully cancelled. Enforce that by asserting the request state to be RequestPending. Signed-off-by: Jacopo Mondi Reviewed-by: Hirokazu Honda Reviewed-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- .../libcamera/internal/tracepoints/request.tp | 8 ++++++ include/libcamera/request.h | 1 + src/libcamera/request.cpp | 28 +++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/include/libcamera/internal/tracepoints/request.tp b/include/libcamera/internal/tracepoints/request.tp index 9e872951374d..9c841b97438a 100644 --- a/include/libcamera/internal/tracepoints/request.tp +++ b/include/libcamera/internal/tracepoints/request.tp @@ -66,6 +66,14 @@ TRACEPOINT_EVENT_INSTANCE( ) ) +TRACEPOINT_EVENT_INSTANCE( + libcamera, + request, + request_cancel, + TP_ARGS( + libcamera::Request *, req + ) +) TRACEPOINT_EVENT( libcamera, diff --git a/include/libcamera/request.h b/include/libcamera/request.h index 4cf5ff3f7d3b..5596901ddd8e 100644 --- a/include/libcamera/request.h +++ b/include/libcamera/request.h @@ -65,6 +65,7 @@ private: friend class PipelineHandler; void complete(); + void cancel(); bool completeBuffer(FrameBuffer *buffer); diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index ce2dd7b17f10..69b2d0172e3d 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -292,6 +292,34 @@ void Request::complete() LIBCAMERA_TRACEPOINT(request_complete, this); } +/** + * \brief Cancel a queued request + * + * 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. + */ +void Request::cancel() +{ + LIBCAMERA_TRACEPOINT(request_cancel, this); + + ASSERT(status_ == RequestPending); + + /* + * We can't simply loop and call completeBuffer() as the erase() call + * invalidates pointers and iterators, so we have to manually cancel the + * buffer from the pending buffers list. + */ + for (auto buffer = pending_.begin(); buffer != pending_.end();) { + (*buffer)->cancel(); + camera_->bufferCompleted.emit(this, *buffer); + buffer = pending_.erase(buffer); + } + + cancelled_ = true; +} + /** * \brief Complete a buffer for the request * \param[in] buffer The buffer that has completed From patchwork Fri May 21 15:42:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12354 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id D6BF5C31FF for ; Fri, 21 May 2021 15:41:52 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B9E7D6892B; Fri, 21 May 2021 17:41:51 +0200 (CEST) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4E4AB6891B for ; Fri, 21 May 2021 17:41:48 +0200 (CEST) Received: (Authenticated sender: jacopo@jmondi.org) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id DFD081BF214; Fri, 21 May 2021 15:41:47 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 21 May 2021 17:42:22 +0200 Message-Id: <20210521154227.60186-4-jacopo@jmondi.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210521154227.60186-1-jacopo@jmondi.org> References: <20210521154227.60186-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 3/8] libcamera: pipeline_handler: Cancel Request on queueing failure X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Capture requests are queued by the PipelineHandler base class to each pipeline handler implementation using the virtual queueRequestDevice() function. However, if the pipeline handler fails to queue the request to the hardware, the request gets silently deleted from the list of queued ones, without notifying application of the error. Reporting to applications that a Request has failed to queue by cancelling and then completing it allows applications to maintain their request-tracking mechanism consistent with the one internal to the library. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/libcamera/pipeline_handler.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index f41b7a7b3308..e507a8bba8a6 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -391,6 +391,8 @@ bool PipelineHandler::hasPendingRequests(const Camera *camera) const * This method queues a capture request to the pipeline handler for processing. * The request is first added to the internal list of queued requests, and * then passed to the pipeline handler with a call to queueRequestDevice(). + * If the pipeline handler fails in queuing the request to the hardware the + * request is cancelled. * * Keeping track of queued requests ensures automatic completion of all requests * when the pipeline handler is stopped with stop(). Request completion shall be @@ -409,8 +411,10 @@ void PipelineHandler::queueRequest(Request *request) request->sequence_ = data->requestSequence_++; int ret = queueRequestDevice(camera, request); - if (ret) - data->queuedRequests_.remove(request); + if (ret) { + request->cancel(); + completeRequest(request); + } } /** From patchwork Fri May 21 15:42:23 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12355 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 142E8C31FF for ; Fri, 21 May 2021 15:41:54 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AB4BF68930; Fri, 21 May 2021 17:41:53 +0200 (CEST) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6AA8A6891B for ; Fri, 21 May 2021 17:41:49 +0200 (CEST) Received: (Authenticated sender: jacopo@jmondi.org) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id 80E331BF20F; Fri, 21 May 2021 15:41:48 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 21 May 2021 17:42:23 +0200 Message-Id: <20210521154227.60186-5-jacopo@jmondi.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210521154227.60186-1-jacopo@jmondi.org> References: <20210521154227.60186-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 4/8] android: camera_device: Replace running_ with CameraState X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The CameraDevice class maintains the camera state in the 'running_' boolean flag to check if the camera has to be started at the first received process_capture_request() call which happens after the camera had been stopped. So far this was correct, as the operations that change the camera could only start or stop the camera, so a simple boolean flag was enough. To prepare to handle the flush() operation that will introduce a new 'flushing' state, replace the simple plain boolean flag with an enumeration of values that define the CameraState. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Reviewed-by: Hirokazu Honda Reviewed-by: Laurent Pinchart --- src/android/camera_device.cpp | 10 +++++----- src/android/camera_device.h | 8 +++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index bd96b355ea92..cf0e5e459213 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -402,7 +402,7 @@ CameraDevice::Camera3RequestDescriptor::Camera3RequestDescriptor( */ CameraDevice::CameraDevice(unsigned int id, std::shared_ptr camera) - : id_(id), running_(false), camera_(std::move(camera)), + : id_(id), state_(CameraStopped), camera_(std::move(camera)), facing_(CAMERA_FACING_FRONT), orientation_(0) { camera_->requestCompleted.connect(this, &CameraDevice::requestComplete); @@ -758,14 +758,14 @@ void CameraDevice::close() void CameraDevice::stop() { - if (!running_) + if (state_ == CameraStopped) return; worker_.stop(); camera_->stop(); descriptors_.clear(); - running_ = false; + state_ = CameraStopped; } void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks) @@ -1844,7 +1844,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques return -EINVAL; /* Start the camera if that's the first request we handle. */ - if (!running_) { + if (state_ == CameraStopped) { worker_.start(); int ret = camera_->start(); @@ -1853,7 +1853,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques return ret; } - running_ = true; + state_ = CameraRunning; } /* diff --git a/src/android/camera_device.h b/src/android/camera_device.h index a34e8a2cd900..995b423c6deb 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -88,6 +88,11 @@ private: int androidFormat; }; + enum State { + CameraStopped, + CameraRunning, + }; + void stop(); int initializeStreamConfigurations(); @@ -114,7 +119,8 @@ private: CameraWorker worker_; - bool running_; + State state_; + std::shared_ptr camera_; std::unique_ptr config_; From patchwork Fri May 21 15:42:24 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12356 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id A0717C31FF for ; Fri, 21 May 2021 15:41:54 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3A9A868921; Fri, 21 May 2021 17:41:54 +0200 (CEST) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9A2546891B for ; Fri, 21 May 2021 17:41:50 +0200 (CEST) Received: (Authenticated sender: jacopo@jmondi.org) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id 9836A1BF214; Fri, 21 May 2021 15:41:49 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 21 May 2021 17:42:24 +0200 Message-Id: <20210521154227.60186-6-jacopo@jmondi.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210521154227.60186-1-jacopo@jmondi.org> References: <20210521154227.60186-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 5/8] android: Replace scoped_lock<> with libcamera::MutexLocker X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The CameraDevice class uses std::scoped_lock<> to guard access to the class' descriptors_ member. std::scoped_lock<> provides a set of features that guarantees safety when locking multiple mutexes in a critical section, while for single locks happening in a scoped block it does not provides benefits compared to the simplest std::unique_lock<> which libcamera provides the MutexLocker type for. Replace usage of std::scoped_lock<> with libcamera::MutexLocker to make the implementation consistent with the rest of the code base. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Reviewed-by: Hirokazu Honda Reviewed-by: Laurent Pinchart --- src/android/camera_device.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index cf0e5e459213..fceae96a8b7c 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -23,6 +23,7 @@ #include "libcamera/internal/formats.h" #include "libcamera/internal/log.h" +#include "libcamera/internal/thread.h" #include "libcamera/internal/utils.h" #include "system/graphics.h" @@ -1947,7 +1948,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques worker_.queueRequest(descriptor.request_.get()); { - std::scoped_lock lock(mutex_); + MutexLocker lock(mutex_); descriptors_[descriptor.request_->cookie()] = std::move(descriptor); } @@ -1958,7 +1959,7 @@ void CameraDevice::requestComplete(Request *request) { decltype(descriptors_)::node_type node; { - std::scoped_lock lock(mutex_); + MutexLocker lock(mutex_); auto it = descriptors_.find(request->cookie()); if (it == descriptors_.end()) { /* From patchwork Fri May 21 15:42:25 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12357 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id E535EC3201 for ; Fri, 21 May 2021 15:41:54 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5FD9F6892D; Fri, 21 May 2021 17:41:54 +0200 (CEST) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 859DB68921 for ; Fri, 21 May 2021 17:41:51 +0200 (CEST) Received: (Authenticated sender: jacopo@jmondi.org) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id ABBF31BF212; Fri, 21 May 2021 15:41:50 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 21 May 2021 17:42:25 +0200 Message-Id: <20210521154227.60186-7-jacopo@jmondi.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210521154227.60186-1-jacopo@jmondi.org> References: <20210521154227.60186-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 6/8] android: Guard access to the camera state X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Guard access to the camera state and the start/stop sequences with a mutex. Currently only stop() and the first call to processCaptureRequest() start and stop the camera, and they're not meant to race with each other. With the introduction of flush() the camera can be stopped concurrently to a processCaptureRequest() call, hence access to the camera state will need to be protected. Prepare for that by guarding the existing paths with a mutex. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Reviewed-by: Hirokazu Honda Reviewed-by: Laurent Pinchart --- src/android/camera_device.cpp | 23 ++++++++++++++--------- src/android/camera_device.h | 1 + 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index fceae96a8b7c..6645c019410e 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -759,6 +759,7 @@ void CameraDevice::close() void CameraDevice::stop() { + MutexLocker cameraLock(cameraMutex_); if (state_ == CameraStopped) return; @@ -1844,17 +1845,21 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques if (!isValidRequest(camera3Request)) return -EINVAL; - /* Start the camera if that's the first request we handle. */ - if (state_ == CameraStopped) { - worker_.start(); + { + MutexLocker cameraLock(cameraMutex_); - int ret = camera_->start(); - if (ret) { - LOG(HAL, Error) << "Failed to start camera"; - return ret; - } + /* Start the camera if that's the first request we handle. */ + if (state_ == CameraStopped) { + worker_.start(); - state_ = CameraRunning; + int ret = camera_->start(); + if (ret) { + LOG(HAL, Error) << "Failed to start camera"; + return ret; + } + + state_ = CameraRunning; + } } /* diff --git a/src/android/camera_device.h b/src/android/camera_device.h index 995b423c6deb..6fe5454c6535 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -119,6 +119,7 @@ private: CameraWorker worker_; + libcamera::Mutex cameraMutex_; /* Protects access to the camera state. */ State state_; std::shared_ptr camera_; From patchwork Fri May 21 15:42:26 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12359 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 27650C3201 for ; Fri, 21 May 2021 15:41:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D1EC968936; Fri, 21 May 2021 17:41:58 +0200 (CEST) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 95D1168927 for ; Fri, 21 May 2021 17:41:52 +0200 (CEST) Received: (Authenticated sender: jacopo@jmondi.org) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id AE5C51BF20E; Fri, 21 May 2021 15:41:51 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 21 May 2021 17:42:26 +0200 Message-Id: <20210521154227.60186-8-jacopo@jmondi.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210521154227.60186-1-jacopo@jmondi.org> References: <20210521154227.60186-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 7/8] android: Rename CameraDevice::mutex_ X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" With the introduction of an additional mutex class member, the name of the existing one is too generic. Rename CameraDevice::mutex_ in CameraDevice::requestsMutex_ and use the libcamera provided libcamera::Mutex type to align the style with the rest of the code base. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Reviewed-by: Hirokazu Honda Reviewed-by: Laurent Pinchart --- src/android/camera_device.cpp | 4 ++-- src/android/camera_device.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 6645c019410e..3fce14035718 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -1953,7 +1953,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques worker_.queueRequest(descriptor.request_.get()); { - MutexLocker lock(mutex_); + MutexLocker requestsLock(requestsMutex_); descriptors_[descriptor.request_->cookie()] = std::move(descriptor); } @@ -1964,7 +1964,7 @@ void CameraDevice::requestComplete(Request *request) { decltype(descriptors_)::node_type node; { - MutexLocker lock(mutex_); + MutexLocker requestsLock(requestsMutex_); auto it = descriptors_.find(request->cookie()); if (it == descriptors_.end()) { /* diff --git a/src/android/camera_device.h b/src/android/camera_device.h index 6fe5454c6535..7cf8e8370387 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -24,6 +24,7 @@ #include "libcamera/internal/buffer.h" #include "libcamera/internal/log.h" #include "libcamera/internal/message.h" +#include "libcamera/internal/thread.h" #include "camera_metadata.h" #include "camera_stream.h" @@ -133,7 +134,7 @@ private: std::map formatsMap_; std::vector streams_; - std::mutex mutex_; /* Protect descriptors_ */ + libcamera::Mutex requestsMutex_; /* Protects descriptors_. */ std::map descriptors_; std::string maker_; From patchwork Fri May 21 15:42:27 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12358 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id D2E9AC31FF for ; Fri, 21 May 2021 15:41:58 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8E87D68920; Fri, 21 May 2021 17:41:58 +0200 (CEST) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 449616891B for ; Fri, 21 May 2021 17:41:53 +0200 (CEST) Received: (Authenticated sender: jacopo@jmondi.org) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id B66911BF215; Fri, 21 May 2021 15:41:52 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 21 May 2021 17:42:27 +0200 Message-Id: <20210521154227.60186-9-jacopo@jmondi.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210521154227.60186-1-jacopo@jmondi.org> References: <20210521154227.60186-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 8/8] android: Implement flush() camera operation X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Implement the flush() camera operation in the CameraDevice class and make it available to the camera framework by implementing the operation wrapper in camera_ops.cpp. The flush() implementation stops the Camera and the worker thread and waits for all in-flight requests to be returned. Stopping the Camera forces all Requests already queued to be returned immediately in error state. As flush() has to wait until all of them have been returned, make it wait on a newly introduced condition variable which is notified by the request completion handler when the queue of pending requests has been exhausted. As flush() can race with processCaptureRequest() protect the requests queueing by introducing a new CameraState::CameraFlushing state that processCaptureRequest() inspects before queuing the Request to the Camera. If flush() has been called while processCaptureRequest() was executing, return the current Request immediately in error state. Protect potentially concurrent calls to close() and configureStreams() by inspecting the CameraState, and force a wait for any flush() call to complete before proceeding. Signed-off-by: Jacopo Mondi Reviewed-by: Hirokazu Honda --- src/android/camera_device.cpp | 90 +++++++++++++++++++++++++++++++++-- src/android/camera_device.h | 9 +++- src/android/camera_ops.cpp | 8 +++- 3 files changed, 100 insertions(+), 7 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 3fce14035718..899afaa49439 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -750,16 +750,65 @@ int CameraDevice::open(const hw_module_t *hardwareModule) void CameraDevice::close() { - streams_.clear(); + MutexLocker cameraLock(cameraMutex_); + if (state_ == CameraFlushing) { + flushed_.wait(cameraLock, [&] { return state_ != CameraStopped; }); + camera_->release(); + return; + } + + streams_.clear(); stop(); camera_->release(); } -void CameraDevice::stop() +/* + * Flush is similar to stop() but sets the camera state to 'flushing' and wait + * until all the in-flight requests have been returned before setting the + * camera state to stopped. + * + * Once flushing is done it unlocks concurrent calls to camera close() and + * configureStreams(). + */ +void CameraDevice::flush() { + { + MutexLocker cameraLock(cameraMutex_); + + if (state_ != CameraRunning) + return; + + worker_.stop(); + camera_->stop(); + state_ = CameraFlushing; + } + + /* + * Now wait for all the in-flight requests to be completed before + * continuing. Stopping the Camera guarantees that all in-flight + * requests are completed in error state. + */ + { + MutexLocker requestsLock(requestsMutex_); + flushing_.wait(requestsLock, [&] { return descriptors_.empty(); }); + } + + /* + * Set state to stopped and unlock close() or configureStreams() that + * might be waiting for flush to be completed. + */ MutexLocker cameraLock(cameraMutex_); + state_ = CameraStopped; + flushed_.notify_one(); +} + +/* Calls to stop() must be protected by cameraMutex_ being held by the caller. */ +void CameraDevice::stop() +{ + ASSERT(state_ != CameraFlushing); + if (state_ == CameraStopped) return; @@ -1581,8 +1630,18 @@ PixelFormat CameraDevice::toPixelFormat(int format) const */ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list) { - /* Before any configuration attempt, stop the camera. */ - stop(); + { + /* + * If a flush is in progress, wait for it to complete and to + * stop the camera, otherwise before any new configuration + * attempt we have to stop the camera explictely. + */ + MutexLocker cameraLock(cameraMutex_); + if (state_ == CameraFlushing) + flushed_.wait(cameraLock, [&] { return state_ != CameraStopped; }); + else + stop(); + } if (stream_list->num_streams == 0) { LOG(HAL, Error) << "No streams in configuration"; @@ -1950,6 +2009,25 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques if (ret) return ret; + /* + * Just before queuing the request, make sure flush() has not + * been called after this function has been executed. In that + * case, immediately return the request with errors. + */ + MutexLocker cameraLock(cameraMutex_); + if (state_ == CameraFlushing || state_ == CameraStopped) { + for (camera3_stream_buffer_t &buffer : descriptor.buffers_) { + buffer.status = CAMERA3_BUFFER_STATUS_ERROR; + buffer.release_fence = buffer.acquire_fence; + } + + notifyError(descriptor.frameNumber_, + descriptor.buffers_[0].stream, + CAMERA3_MSG_ERROR_REQUEST); + + return 0; + } + worker_.queueRequest(descriptor.request_.get()); { @@ -1979,6 +2057,10 @@ void CameraDevice::requestComplete(Request *request) return; } + /* Release flush if all the pending requests have been completed. */ + if (descriptors_.empty()) + flushing_.notify_one(); + node = descriptors_.extract(it); } Camera3RequestDescriptor &descriptor = node.mapped(); diff --git a/src/android/camera_device.h b/src/android/camera_device.h index 7cf8e8370387..e1b3bf7d30f2 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 #include #include #include @@ -42,6 +43,7 @@ public: int open(const hw_module_t *hardwareModule); void close(); + void flush(); unsigned int id() const { return id_; } camera3_device_t *camera3Device() { return &camera3Device_; } @@ -92,6 +94,7 @@ private: enum State { CameraStopped, CameraRunning, + CameraFlushing, }; void stop(); @@ -120,8 +123,9 @@ private: CameraWorker worker_; - libcamera::Mutex cameraMutex_; /* Protects access to the camera state. */ + libcamera::Mutex cameraMutex_; /* Protects the camera state and flushed_. */ State state_; + std::condition_variable flushed_; std::shared_ptr camera_; std::unique_ptr config_; @@ -134,8 +138,9 @@ private: std::map formatsMap_; std::vector streams_; - libcamera::Mutex requestsMutex_; /* Protects descriptors_. */ + libcamera::Mutex requestsMutex_; /* Protects descriptors_ and flushing_. */ std::map descriptors_; + std::condition_variable flushing_; std::string maker_; std::string model_; diff --git a/src/android/camera_ops.cpp b/src/android/camera_ops.cpp index 696e80436821..8a3cfa175ff5 100644 --- a/src/android/camera_ops.cpp +++ b/src/android/camera_ops.cpp @@ -66,8 +66,14 @@ static void hal_dev_dump([[maybe_unused]] const struct camera3_device *dev, { } -static int hal_dev_flush([[maybe_unused]] const struct camera3_device *dev) +static int hal_dev_flush(const struct camera3_device *dev) { + if (!dev) + return -EINVAL; + + CameraDevice *camera = reinterpret_cast(dev->priv); + camera->flush(); + return 0; }