From patchwork Mon Jun 29 16:30:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 27128 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 41EEDC3261 for ; Mon, 29 Jun 2026 16:31:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CAA6D65FBE; Mon, 29 Jun 2026 18:31:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="iK8xCcNk"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 272E965F76 for ; Mon, 29 Jun 2026 18:30:34 +0200 (CEST) Received: from pb-laptop.local (185.221.140.128.nat.pool.zt.hu [185.221.140.128]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E51D18D4 for ; Mon, 29 Jun 2026 18:29:50 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1782750591; bh=7YWY/lzWSfNS9Eie2jc2jCVI6xr/Oc8SVJB7lCQTPJg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=iK8xCcNkci743VuOe8KPjQvve206BJehzNns7ZSE0eEtwF4qyiZzYbXpHoWUq/WXn cJBcumGLE6F5Ua5/BcSVrD06KrxBFkPYwjo2mpJkeQrMyYlZ7IBii7A3XpJcTNArfY hSTmCG03640ioDEe7Fn/1CIxJrXf9m+9UxyZiX9U= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Subject: [RFC PATCH v1 52/54] android: Use camera buffer pool Date: Mon, 29 Jun 2026 18:30:15 +0200 Message-ID: <20260629163017.863145-53-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260629163017.863145-1-barnabas.pocze@ideasonboard.com> References: <20260629163017.863145-1-barnabas.pocze@ideasonboard.com> MIME-Version: 1.0 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" Convert the Android HAL to use the camera buffer pool. This involves switching to camera device version 3.6, the newer style of buffer management[0], and using `{return,request}_stream_buffers()`, and furthermore implementing the `signal_stream_flush()` function. These changes are largely untested and were written based on the documentation in the header files as well as the code in [1]. TODO: this removes numFds and numInt checks, are those important?! [0]: https://source.android.com/docs/core/camera/buffer-management-api [1]: https://android.googlesource.com/platform/hardware/interfaces/+/refs/tags/android-17.0.0_r1/camera/device/3.5/default/CameraDeviceSession.cpp#174 Signed-off-by: Barnabás Pőcze --- src/android/camera_capabilities.cpp | 3 + src/android/camera_device.cpp | 250 ++++++++++++++++++++++------ src/android/camera_device.h | 4 + src/android/camera_hal_manager.cpp | 2 +- src/android/camera_ops.cpp | 14 +- src/android/camera_request.cpp | 20 +-- src/android/camera_request.h | 9 +- 7 files changed, 227 insertions(+), 75 deletions(-) diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp index 6b58dd5548..feb1cae490 100644 --- a/src/android/camera_capabilities.cpp +++ b/src/android/camera_capabilities.cpp @@ -1453,6 +1453,9 @@ int CameraCapabilities::initializeStaticMetadata() LOG(HAL, Info) << "Hardware level: " << hwLevelStrings.find(hwLevel_)->second; + staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, + ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5); + staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, std::vector(availableCharacteristicsKeys_.begin(), availableCharacteristicsKeys_.end())); diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 09aa129e0a..97f2e1c515 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -394,7 +394,7 @@ int CameraDevice::open(const hw_module_t *hardwareModule) /* Initialize the hw_device_t in the instance camera3_module_t. */ camera3Device_.common.tag = HARDWARE_DEVICE_TAG; - camera3Device_.common.version = CAMERA_DEVICE_API_VERSION_3_3; + camera3Device_.common.version = CAMERA_DEVICE_API_VERSION_3_6; camera3Device_.common.module = (hw_module_t *)hardwareModule; camera3Device_.common.close = hal_dev_close; @@ -434,9 +434,45 @@ void CameraDevice::flush() void CameraDevice::stop() { MutexLocker stateLock(stateMutex_); + std::vector streamBuffers; + + camera_->bufferCompleted.connect(&streamBuffers, [&](libcamera::Request *request, [[maybe_unused]] const libcamera::Stream *stream, libcamera::FrameBuffer *fb) { + if (request) + return; + + /* Collect buffers from the pool that were not used for a request. */ + + std::unique_ptr buffer( + reinterpret_cast(fb->cookie())); + fb->setCookie(0); + + streamBuffers.push_back(std::move(*buffer)); + }); camera_->stop(); + camera_->bufferCompleted.disconnect(&streamBuffers); + + /* Return buffers that were unused by requests. */ + { + std::vector returnBuffers; + returnBuffers.reserve(streamBuffers.size()); + + for (auto &buffer : streamBuffers) { + if (buffer.internalBuffer) + buffer.stream->putBuffer(buffer.internalBuffer); + + buffer.status = Camera3RequestDescriptor::Status::Error; + returnBuffers.push_back(buffer.prepareToReturn()); + } + + auto bufferPtrs = std::make_unique(returnBuffers.size()); + for (const auto &[i, buffers] : utils::enumerate(returnBuffers)) + bufferPtrs[i] = &buffers; + + callbacks_->return_stream_buffers(callbacks_, returnBuffers.size(), bufferPtrs.get()); + } + { MutexLocker descriptorsLock(descriptorsMutex_); descriptors_ = {}; @@ -879,7 +915,7 @@ bool CameraDevice::isValidRequest(camera3_capture_request_t *camera3Request) con if (!camera3Request->num_output_buffers || !camera3Request->output_buffers) { - LOG(HAL, Error) << "No output buffers provided"; + LOG(HAL, Error) << "No streams requested"; return false; } @@ -892,25 +928,8 @@ bool CameraDevice::isValidRequest(camera3_capture_request_t *camera3Request) con for (uint32_t i = 0; i < camera3Request->num_output_buffers; i++) { const camera3_stream_buffer_t &outputBuffer = camera3Request->output_buffers[i]; - if (!outputBuffer.buffer || !(*outputBuffer.buffer)) { - LOG(HAL, Error) << "Invalid native handle"; - return false; - } - - const native_handle_t *handle = *outputBuffer.buffer; - constexpr int kNativeHandleMaxFds = 1024; - if (handle->numFds < 0 || handle->numFds > kNativeHandleMaxFds) { - LOG(HAL, Error) - << "Invalid number of fds (" << handle->numFds - << ") in buffer " << i; - return false; - } - - constexpr int kNativeHandleMaxInts = 1024; - if (handle->numInts < 0 || handle->numInts > kNativeHandleMaxInts) { - LOG(HAL, Error) - << "Invalid number of ints (" << handle->numInts - << ") in buffer " << i; + if (outputBuffer.buffer || *outputBuffer.buffer) { + LOG(HAL, Error) << "Unexpected native buffer handle"; return false; } @@ -940,6 +959,11 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques if (!isValidRequest(camera3Request)) return -EINVAL; + const Span buffers{ + camera3Request->output_buffers, + camera3Request->num_output_buffers + }; + /* * Save the request descriptors for use at completion time. * The descriptor and the associated memory reserved here are freed @@ -960,7 +984,73 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques descriptor->settings_ = lastSettings_; LOG(HAL, Debug) << "Queueing request " << descriptor->request_->cookie() - << " with " << descriptor->buffers_.size() << " streams"; + << " with " << buffers.size() << " streams"; + + std::unordered_map> streamBuffers; + + utils::scope_exit bufferGuard([&] { + std::vector returnBuffers; + + for (auto &[stream, buffer] : streamBuffers) { + if (!buffer) + continue; + + returnBuffers.push_back(buffer->prepareToReturn()); + } + + auto bufferPtrs = std::make_unique(returnBuffers.size()); + for (const auto &[i, buffer] : utils::enumerate(returnBuffers)) + bufferPtrs[i] = &buffer; + + callbacks_->return_stream_buffers(callbacks_, returnBuffers.size(), bufferPtrs.get()); + }); + + { + uint32_t count = buffers.size(); + + auto bufferResults = std::make_unique(count); + auto bufferRequests = std::make_unique(count); + auto returnedBuffers = std::make_unique(count); + + for (size_t i = 0; i < count; i++) { + bufferRequests[i] = { + .stream = buffers[i].stream, + .num_buffers_requested = 1, + }; + + /* Must provide storage for result. */ + bufferResults[i].output_buffers = &returnedBuffers[i]; + } + + auto ret = callbacks_->request_stream_buffers(callbacks_, + count, bufferRequests.get(), + &count, bufferResults.get()); + + for (size_t i = 0; i < count; i++) { + auto &result = bufferResults[i]; + if (result.status != CAMERA3_PS_BUF_REQ_OK) + continue; + + auto *stream = static_cast(result.stream->priv); + ASSERT(result.num_output_buffers == 1); + + [[maybe_unused]] auto [it, inserted] = streamBuffers.try_emplace(stream, + std::make_unique( + stream, result.output_buffers[0] + ) + ); + ASSERT(inserted); + } + + if (ret != CAMERA3_BUF_REQ_OK) { + /* + * \todo Improve error handling. For now every stream must get + * a buffer successfully. This is checked here so that any successfully + * allocated buffer is returned by `bufferGuard`. + */ + return -ENOBUFS; + } + } /* * Process all the Direct and Internal streams first, they map directly @@ -970,8 +1060,8 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques * Since requestedStreams is an std:set<>, no duplications can happen. */ std::set requestedStreams; - for (const auto &[i, buffer] : utils::enumerate(descriptor->buffers_)) { - CameraStream *cameraStream = buffer.stream; + for (const auto &[i, buffer] : utils::enumerate(buffers)) { + auto *cameraStream = static_cast(buffer.stream->priv); camera3_stream_t *camera3Stream = cameraStream->camera3Stream(); std::stringstream ss; @@ -986,7 +1076,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques * and add them to the Request if required. */ FrameBuffer *frameBuffer = nullptr; - UniqueFD acquireFence; + auto &streamBuffer = *streamBuffers.at(cameraStream); MutexLocker lock(descriptor->streamsProcessMutex_); @@ -1002,12 +1092,11 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques * associate it with the Camera3RequestDescriptor for * lifetime management only. */ - buffer.frameBuffer = - createFrameBuffer(*buffer.camera3Buffer, + streamBuffer.frameBuffer = + createFrameBuffer(*streamBuffer.camera3Buffer, cameraStream->configuration().pixelFormat, cameraStream->configuration().size); - frameBuffer = buffer.frameBuffer.get(); - acquireFence = std::move(buffer.fence); + frameBuffer = streamBuffer.frameBuffer.get(); LOG(HAL, Debug) << ss.str() << " (direct)"; break; @@ -1020,11 +1109,10 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques * once it has been processed. */ frameBuffer = cameraStream->getBuffer(); - buffer.internalBuffer = frameBuffer; + streamBuffer.internalBuffer = frameBuffer; LOG(HAL, Debug) << ss.str() << " (internal)"; - descriptor->pendingStreamsToProcess_.insert( - { cameraStream, &buffer }); + descriptor->pendingStreamsToProcess_.insert(cameraStream); break; } @@ -1033,10 +1121,6 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques return -ENOMEM; } - auto fence = std::make_unique(std::move(acquireFence)); - descriptor->request_->addBuffer(cameraStream->stream(), - frameBuffer, std::move(fence)); - requestedStreams.insert(cameraStream); } @@ -1045,8 +1129,8 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques * because their corresponding direct source stream is not part of this * particular request, add one here. */ - for (const auto &[i, buffer] : utils::enumerate(descriptor->buffers_)) { - CameraStream *cameraStream = buffer.stream; + for (const auto &[i, buffer] : utils::enumerate(buffers)) { + auto *cameraStream = static_cast(buffer.stream->priv); camera3_stream_t *camera3Stream = cameraStream->camera3Stream(); if (cameraStream->type() != CameraStream::Type::Mapped) @@ -1060,7 +1144,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques << " (mapped)"; MutexLocker lock(descriptor->streamsProcessMutex_); - descriptor->pendingStreamsToProcess_.insert({ cameraStream, &buffer }); + descriptor->pendingStreamsToProcess_.insert(cameraStream); /* * Make sure the CameraStream this stream is mapped on has been @@ -1071,15 +1155,14 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques if (requestedStreams.find(sourceStream) != requestedStreams.end()) continue; + auto &streamBuffer = *streamBuffers.at(cameraStream); + /* * If that's not the case, we need to add a buffer to the request * for this stream. */ FrameBuffer *frameBuffer = cameraStream->getBuffer(); - buffer.internalBuffer = frameBuffer; - - descriptor->request_->addBuffer(sourceStream->stream(), - frameBuffer); + streamBuffer.internalBuffer = frameBuffer; requestedStreams.insert(sourceStream); } @@ -1123,6 +1206,38 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques state_ = State::Running; } + for (auto *cameraStream : requestedStreams) { + auto &buffer = streamBuffers.at(cameraStream); + const Stream *stream = nullptr; + FrameBuffer *fb = nullptr; + UniqueFD acquireFence; + + switch (cameraStream->type()) { + case CameraStream::Type::Direct: + stream = cameraStream->stream(); + fb = buffer->frameBuffer.get(); + acquireFence = std::move(buffer->fence); + break; + case CameraStream::Type::Internal: + stream = cameraStream->stream(); + fb = buffer->internalBuffer; + break; + case CameraStream::Type::Mapped: + stream = cameraStream->sourceStream()->stream(); + fb = buffer->internalBuffer; + break; + default: + ASSERT(false); + continue; + } + + + fb->setCookie(reinterpret_cast(buffer.release())); + camera_->addBuffer(stream, fb, std::make_unique(std::move(acquireFence))); + + descriptor->request_->enableStream(stream, true); + } + Request *request = descriptor->request_.get(); { @@ -1140,6 +1255,17 @@ void CameraDevice::requestComplete(Request *request) Camera3RequestDescriptor *descriptor = reinterpret_cast(request->cookie()); + std::vector> buffers; + + for (const auto &[stream, fb] : request->buffers()) { + std::unique_ptr buffer( + reinterpret_cast(fb->cookie())); + fb->setCookie(0); + + buffer->request = descriptor; + descriptor->buffers_.push_back(std::move(*buffer)); + } + /* * Prepare the capture result for the Android camera stack. * @@ -1208,18 +1334,17 @@ void CameraDevice::requestComplete(Request *request) */ auto iter = descriptor->pendingStreamsToProcess_.begin(); while (iter != descriptor->pendingStreamsToProcess_.end()) { - CameraStream *stream = iter->first; - Camera3RequestDescriptor::StreamBuffer *buffer = iter->second; - - FrameBuffer *src = request->findBuffer(stream->stream()); - if (!src) { + CameraStream *stream = *iter; + auto it = std::find_if(descriptor->buffers_.begin(), descriptor->buffers_.end(), + [&](const auto &d) { return d.stream == stream; }); + if (it == descriptor->buffers_.end()) { LOG(HAL, Error) << "Failed to find a source stream buffer"; - setBufferStatus(*buffer, Camera3RequestDescriptor::Status::Error); iter = descriptor->pendingStreamsToProcess_.erase(iter); continue; } - buffer->srcBuffer = src; + auto *buffer = &*it; + buffer->srcBuffer = buffer->internalBuffer; ++iter; int ret = stream->process(buffer); @@ -1276,6 +1401,8 @@ void CameraDevice::completeDescriptor(Camera3RequestDescriptor *descriptor) */ void CameraDevice::sendCaptureResults() { + bool notify = false; + while (!descriptors_.empty() && !descriptors_.front()->isPending()) { auto descriptor = std::move(descriptors_.front()); descriptors_.pop(); @@ -1301,7 +1428,11 @@ void CameraDevice::sendCaptureResults() captureResult.partial_result = 1; callbacks_->process_capture_result(callbacks_, &captureResult); + notify = true; } + + if (notify) + descriptorsCv_.notify_all(); } void CameraDevice::setBufferStatus(Camera3RequestDescriptor::StreamBuffer &streamBuffer, @@ -1357,6 +1488,25 @@ void CameraDevice::streamProcessingComplete(Camera3RequestDescriptor::StreamBuff completeDescriptor(streamBuffer->request); } +void CameraDevice::signalStreamFlush([[maybe_unused]] libcamera::Span streams) +{ + /* + * `streams` is ignored because it is not possible to selectively + * retrieve all buffers a given stream. + */ + + { + libcamera::MutexLocker locker(descriptorsMutex_); + + descriptorsCv_.wait(locker, [&]() LIBCAMERA_TSA_REQUIRES(descriptorsMutex_) { + return descriptors_.empty(); + }); + } + + /* \todo Is stopping ok here? */ + stop(); +} + std::string CameraDevice::logPrefix() const { return "'" + camera_->id() + "'"; diff --git a/src/android/camera_device.h b/src/android/camera_device.h index 194ca30304..c0f8b88ee5 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,8 @@ public: void streamProcessingComplete(Camera3RequestDescriptor::StreamBuffer *bufferStream, Camera3RequestDescriptor::Status status); + void signalStreamFlush(libcamera::Span streams); + protected: std::string logPrefix() const override; @@ -118,6 +121,7 @@ private: std::vector streams_; libcamera::Mutex descriptorsMutex_ LIBCAMERA_TSA_ACQUIRED_AFTER(stateMutex_); + libcamera::ConditionVariable descriptorsCv_; std::queue> descriptors_ LIBCAMERA_TSA_GUARDED_BY(descriptorsMutex_); diff --git a/src/android/camera_hal_manager.cpp b/src/android/camera_hal_manager.cpp index a7a2571754..6b9a1007ee 100644 --- a/src/android/camera_hal_manager.cpp +++ b/src/android/camera_hal_manager.cpp @@ -259,7 +259,7 @@ int CameraHalManager::getCameraInfo(unsigned int id, struct camera_info *info) info->facing = camera->facing(); info->orientation = camera->orientation(); - info->device_version = CAMERA_DEVICE_API_VERSION_3_3; + info->device_version = CAMERA_DEVICE_API_VERSION_3_6; info->resource_cost = 0; info->static_camera_characteristics = camera->getStaticMetadata(); info->conflicting_devices = nullptr; diff --git a/src/android/camera_ops.cpp b/src/android/camera_ops.cpp index 1656fac7ab..d8f0f715b2 100644 --- a/src/android/camera_ops.cpp +++ b/src/android/camera_ops.cpp @@ -77,6 +77,18 @@ static int hal_dev_flush(const struct camera3_device *dev) return 0; } +static void hal_dev_signal_stream_flush(const struct camera3_device *dev, + uint32_t num_streams, + const camera3_stream_t* const* streams) +{ + if (!dev) + return; + + CameraDevice *camera = reinterpret_cast(dev->priv); + + camera->signalStreamFlush({ streams, num_streams }); +} + int hal_dev_close(hw_device_t *hw_device) { if (!hw_device) @@ -99,7 +111,7 @@ camera3_device_ops hal_dev_ops = { .get_metadata_vendor_tag_ops = nullptr, .dump = hal_dev_dump, .flush = hal_dev_flush, - .signal_stream_flush = nullptr, + .signal_stream_flush = hal_dev_signal_stream_flush, .is_reconfiguration_required = nullptr, .reserved = {}, }; diff --git a/src/android/camera_request.cpp b/src/android/camera_request.cpp index 6dfe692f09..91bd618006 100644 --- a/src/android/camera_request.cpp +++ b/src/android/camera_request.cpp @@ -114,21 +114,6 @@ Camera3RequestDescriptor::Camera3RequestDescriptor( { frameNumber_ = camera3Request->frame_number; - /* Copy the camera3 request stream information for later access. */ - const Span buffers{ - camera3Request->output_buffers, - camera3Request->num_output_buffers - }; - - buffers_.reserve(buffers.size()); - - for (const camera3_stream_buffer_t &buffer : buffers) { - CameraStream *stream = - static_cast(buffer.stream->priv); - - buffers_.emplace_back(stream, buffer, this); - } - /* Clone the controls associated with the camera3 request. */ settings_ = CameraMetadata(camera3Request->settings); @@ -180,10 +165,9 @@ Camera3RequestDescriptor::~Camera3RequestDescriptor() = default; * \brief Back pointer to the Camera3RequestDescriptor to which the StreamBuffer belongs */ Camera3RequestDescriptor::StreamBuffer::StreamBuffer( - CameraStream *cameraStream, const camera3_stream_buffer_t &buffer, - Camera3RequestDescriptor *requestDescriptor) + CameraStream *cameraStream, const camera3_stream_buffer_t &buffer) : stream(cameraStream), camera3Buffer(buffer.buffer), - fence(buffer.acquire_fence), request(requestDescriptor) + fence(buffer.acquire_fence) { } diff --git a/src/android/camera_request.h b/src/android/camera_request.h index 164b095b28..1a5393b1d6 100644 --- a/src/android/camera_request.h +++ b/src/android/camera_request.h @@ -7,8 +7,8 @@ #pragma once -#include #include +#include #include #include @@ -36,8 +36,7 @@ public: struct StreamBuffer { StreamBuffer(CameraStream *stream, - const camera3_stream_buffer_t &buffer, - Camera3RequestDescriptor *request); + const camera3_stream_buffer_t &buffer); ~StreamBuffer(); StreamBuffer(StreamBuffer &&); @@ -53,14 +52,14 @@ public: libcamera::FrameBuffer *internalBuffer = nullptr; const libcamera::FrameBuffer *srcBuffer = nullptr; std::unique_ptr dstBuffer; - Camera3RequestDescriptor *request; + Camera3RequestDescriptor *request = nullptr; private: LIBCAMERA_DISABLE_COPY(StreamBuffer) }; /* Keeps track of streams requiring post-processing. */ - std::map pendingStreamsToProcess_ + std::unordered_set pendingStreamsToProcess_ LIBCAMERA_TSA_GUARDED_BY(streamsProcessMutex_); libcamera::Mutex streamsProcessMutex_;