[libcamera-devel] [PATCH 6/6] android: Implement partial result feature based on partialResultCompleted signal
Han-Lin Chen
hanlinchen at chromium.org
Tue Jul 26 20:25:00 CEST 2022
To impelement partial result feature. The patch includes the following:
1. Set Camera::RequestCompletionMode to Immediately.
2. Return partial results on Camera::partialResultCompleted signal, and recycle
requests on Camera::requestCompleted.
3. Refactory usage of pendingStreamsToProcess_ and starts post-processing on
partial result receved.
Signed-off-by: Han-Lin Chen <hanlinchen at chromium.org>
---
src/android/camera_capabilities.cpp | 2 +-
src/android/camera_capabilities.h | 2 +
src/android/camera_device.cpp | 687 ++++++++++++++---------
src/android/camera_device.h | 23 +-
src/android/camera_request.cpp | 50 +-
src/android/camera_request.h | 46 +-
src/android/jpeg/post_processor_jpeg.cpp | 2 +-
7 files changed, 522 insertions(+), 290 deletions(-)
diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
index 64bd8dde..6ed040d0 100644
--- a/src/android/camera_capabilities.cpp
+++ b/src/android/camera_capabilities.cpp
@@ -1374,7 +1374,7 @@ int CameraCapabilities::initializeStaticMetadata()
staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);
/* Request static metadata. */
- int32_t partialResultCount = 1;
+ int32_t partialResultCount = MaxMetadataPackIndex;
staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
partialResultCount);
diff --git a/src/android/camera_capabilities.h b/src/android/camera_capabilities.h
index 6f66f221..b8fc20f1 100644
--- a/src/android/camera_capabilities.h
+++ b/src/android/camera_capabilities.h
@@ -23,6 +23,8 @@
class CameraCapabilities
{
public:
+ static constexpr int32_t MaxMetadataPackIndex = 64;
+
CameraCapabilities() = default;
int initialize(std::shared_ptr<libcamera::Camera> camera,
diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index a14d5de9..6d79fd34 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -24,9 +24,8 @@
#include <libcamera/formats.h>
#include <libcamera/property_ids.h>
-#include "system/graphics.h"
-
#include "camera_buffer.h"
+#include "camera_capabilities.h"
#include "camera_hal_config.h"
#include "camera_ops.h"
#include "camera_request.h"
@@ -249,7 +248,11 @@ CameraDevice::CameraDevice(unsigned int id, std::shared_ptr<Camera> camera)
: id_(id), state_(State::Stopped), camera_(std::move(camera)),
facing_(CAMERA_FACING_FRONT), orientation_(0)
{
+ /* Set RequestCompletionMode to Immediately to send result early */
+ camera_->setRequestCompletionMode(Camera::Immediately);
+
camera_->requestCompleted.connect(this, &CameraDevice::requestComplete);
+ camera_->partialResultCompleted.connect(this, &CameraDevice::partialResultComplete);
maker_ = "libcamera";
model_ = "cameraModel";
@@ -439,7 +442,7 @@ void CameraDevice::stop()
{
MutexLocker descriptorsLock(descriptorsMutex_);
- descriptors_ = {};
+ descriptors_.clear();
}
streams_.clear();
@@ -855,14 +858,30 @@ int CameraDevice::processControls(Camera3RequestDescriptor *descriptor)
return 0;
}
-void CameraDevice::abortRequest(Camera3RequestDescriptor *descriptor) const
+void CameraDevice::abortRequest(Camera3RequestDescriptor *descriptor)
{
- notifyError(descriptor->frameNumber_, nullptr, CAMERA3_MSG_ERROR_REQUEST);
+ Camera3ResultDescriptor *result = new Camera3ResultDescriptor(descriptor);
- for (auto &buffer : descriptor->buffers_)
- buffer.status = StreamBuffer::Status::Error;
+ for (auto &buffer : descriptor->buffers_) {
+ setBufferStatus(buffer, StreamBuffer::Status::Error);
+ result->buffers_.emplace_back(&buffer);
+ }
- descriptor->status_ = Camera3RequestDescriptor::Status::Error;
+ {
+ MutexLocker lock(descriptor->resultsMutex_);
+ descriptor->status_ = Camera3RequestDescriptor::Status::Flushed;
+ descriptor->results_.emplace_back(result);
+ }
+
+ /*
+ * After CAMERA3_MSG_ERROR_REQUEST is notified, for a given frame,
+ * only process_capture_results with buffers in
+ * CAMERA3_BUFFER_STATUS_ERROR are allowed. No further notifys or
+ * process_capture_result with non-null metadata is allowed.
+ */
+ notifyError(descriptor->frameNumber_, nullptr, CAMERA3_MSG_ERROR_REQUEST);
+
+ completeResultDescriptor(result);
}
bool CameraDevice::isValidRequest(camera3_capture_request_t *camera3Request) const
@@ -962,9 +981,9 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
* to a libcamera stream. Streams of type Mapped will be handled later.
*
* Collect the CameraStream associated to each requested capture stream.
- * Since requestedStreams is an std:set<>, no duplications can happen.
+ * Since requestedStreams is an std:map<>, no duplications can happen.
*/
- std::set<CameraStream *> requestedStreams;
+ std::map<CameraStream *, libcamera::FrameBuffer *> requestedStreams;
for (const auto &[i, buffer] : utils::enumerate(descriptor->buffers_)) {
CameraStream *cameraStream = buffer.stream;
camera3_stream_t *camera3Stream = cameraStream->camera3Stream();
@@ -983,8 +1002,6 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
FrameBuffer *frameBuffer = nullptr;
UniqueFD acquireFence;
- MutexLocker lock(descriptor->streamsProcessMutex_);
-
switch (cameraStream->type()) {
case CameraStream::Type::Mapped:
/* Mapped streams will be handled in the next loop. */
@@ -1016,10 +1033,8 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
*/
frameBuffer = cameraStream->getBuffer();
buffer.internalBuffer = frameBuffer;
+ buffer.srcBuffer = frameBuffer;
LOG(HAL, Debug) << ss.str() << " (internal)";
-
- descriptor->pendingStreamsToProcess_.insert(
- { cameraStream, &buffer });
break;
}
@@ -1032,7 +1047,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
descriptor->request_->addBuffer(cameraStream->stream(),
frameBuffer, std::move(fence));
- requestedStreams.insert(cameraStream);
+ requestedStreams[cameraStream] = frameBuffer;
}
/*
@@ -1054,29 +1069,38 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
<< cameraStream->configuration().pixelFormat << "]"
<< " (mapped)";
- MutexLocker lock(descriptor->streamsProcessMutex_);
- descriptor->pendingStreamsToProcess_.insert({ cameraStream, &buffer });
-
/*
* Make sure the CameraStream this stream is mapped on has been
* added to the request.
*/
CameraStream *sourceStream = cameraStream->sourceStream();
+
ASSERT(sourceStream);
- if (requestedStreams.find(sourceStream) != requestedStreams.end())
+ ASSERT(sourceStream->type() == CameraStream::Type::Direct);
+
+ /*
+ * If the buffer for the source stream is requested, use its
+ * framebuffer as the source buffer for post-processing.
+ */
+ auto iter = requestedStreams.find(sourceStream);
+ if (iter != requestedStreams.end()) {
+ buffer.srcBuffer = iter->second;
continue;
+ }
/*
- * If that's not the case, we need to add a buffer to the request
- * for this stream.
+ * If that's not the case, we need to add an internal buffer
+ * to the request for this stream.
+ *
+ * \todo Handle the case that multiple mapped streams need to
+ * create one internal buffer for the same source stream.
*/
- FrameBuffer *frameBuffer = cameraStream->getBuffer();
+ FrameBuffer *frameBuffer = sourceStream->getBuffer();
buffer.internalBuffer = frameBuffer;
+ buffer.srcBuffer = frameBuffer;
descriptor->request_->addBuffer(sourceStream->stream(),
frameBuffer, nullptr);
-
- requestedStreams.erase(sourceStream);
}
/*
@@ -1098,11 +1122,9 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
Camera3RequestDescriptor *rawDescriptor = descriptor.get();
{
MutexLocker descriptorsLock(descriptorsMutex_);
- descriptors_.push(std::move(descriptor));
+ descriptors_.push_back(std::move(descriptor));
}
abortRequest(rawDescriptor);
- completeDescriptor(rawDescriptor);
-
return 0;
}
@@ -1120,7 +1142,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
{
MutexLocker descriptorsLock(descriptorsMutex_);
- descriptors_.push(std::move(descriptor));
+ descriptors_.push_back(std::move(descriptor));
}
camera_->queueRequest(request);
@@ -1128,131 +1150,238 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
return 0;
}
-void CameraDevice::requestComplete(Request *request)
+void CameraDevice::partialResultComplete(Request *request, Result *result)
{
Camera3RequestDescriptor *descriptor =
reinterpret_cast<Camera3RequestDescriptor *>(request->cookie());
- /*
- * Prepare the capture result for the Android camera stack.
- *
- * The buffer status is set to Success and later changed to Error if
- * post-processing/compression fails.
- */
- for (auto &buffer : descriptor->buffers_) {
- CameraStream *stream = buffer.stream;
+ if (result->buffers().empty() && result->metadata().empty())
+ LOG(HAL, Fatal)
+ << "Partial result should have at least buffer or metadata "
+ << descriptor->frameNumber_;
+ Camera3ResultDescriptor *camera3Result = new Camera3ResultDescriptor(descriptor);
+
+ const ControlList &metadata = result->metadata();
+ if (!metadata.empty()) {
/*
- * Streams of type Direct have been queued to the
- * libcamera::Camera and their acquire fences have
- * already been waited on by the library.
- *
- * Acquire fences of streams of type Internal and Mapped
- * will be handled during post-processing.
+ * Notify shutter as soon as we have received SensorTimestamp.
*/
- if (stream->type() == CameraStream::Type::Direct) {
- /* If handling of the fence has failed restore buffer.fence. */
- std::unique_ptr<Fence> fence = buffer.frameBuffer->releaseFence();
- if (fence)
- buffer.fence = fence->release();
+
+ const auto ×tamp = metadata.get(controls::SensorTimestamp);
+ if (timestamp) {
+ notifyShutter(descriptor->frameNumber_, *timestamp);
+ LOG(HAL, Debug) << "Request " << request->cookie() << " notifies shutter";
}
- buffer.status = StreamBuffer::Status::Success;
+
+ camera3Result->resultMetadata_ = getDynamicResultMetadata(metadata);
}
- /*
- * 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 " << request->cookie()
- << " not successfully completed: "
- << request->status();
+ MutexLocker locker(camera3Result->streamsProcessMutex_);
- abortRequest(descriptor);
- completeDescriptor(descriptor);
+ for (auto &buffer : descriptor->buffers_) {
+ CameraStream *cameraStream = buffer.stream;
+ for (auto *frameBuffer : result->buffers()) {
+ if (buffer.srcBuffer != frameBuffer &&
+ buffer.frameBuffer.get() != frameBuffer)
+ continue;
- return;
+ buffer.result = camera3Result;
+ camera3Result->buffers_.emplace_back(&buffer);
+
+ StreamBuffer::Status status = StreamBuffer::Status::Success;
+ if (frameBuffer->metadata().status != FrameMetadata::FrameSuccess) {
+ status = StreamBuffer::Status::Error;
+ }
+ setBufferStatus(buffer, status);
+
+ switch (cameraStream->type()) {
+ case CameraStream::Type::Direct: {
+ ASSERT(buffer.frameBuffer.get() == frameBuffer);
+ /*
+ * Streams of type Direct have been queued to the
+ * libcamera::Camera and their acquire fences have
+ * already been waited on by the library.
+ */
+ std::unique_ptr<Fence> fence = buffer.frameBuffer->releaseFence();
+ if (fence)
+ buffer.fence = fence->release();
+ break;
+ }
+ case CameraStream::Type::Mapped:
+ case CameraStream::Type::Internal:
+ ASSERT(buffer.srcBuffer == frameBuffer);
+ if (status == StreamBuffer::Status::Error) {
+ /*
+ * If the framebuffer is internal to CameraStream return
+ * it back now that we're done processing it.
+ */
+ if (buffer.internalBuffer)
+ returnInternalBuffer(buffer);
+ break;
+ }
+
+ /*
+ * Acquire fences of streams of type Internal and Mapped
+ * will be handled during post-processing.
+ */
+ buffer.srcBuffer = frameBuffer;
+ camera3Result->pendingBuffersToProcess_.emplace_back(&buffer);
+
+ if (cameraStream->isJpegStream()) {
+ generateJpegExifMetadata(descriptor, &buffer);
+
+ /*
+ * Allocate for post-processor to fill
+ * in JPEG related metadata.
+ */
+ if (!camera3Result->resultMetadata_)
+ camera3Result->resultMetadata_ = getDynamicResultMetadata(metadata);
+ }
+ break;
+ }
+ }
+ }
+
+ {
+ /*
+ * Adding result to the request before sending it to the
+ * post-processing threads, so the streamProcessingComplete()
+ * slot can safely call compeleteResult().
+ */
+ MutexLocker lock(descriptor->resultsMutex_);
+ descriptor->results_.emplace_back(camera3Result);
}
/*
- * 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.
+ * Queue all the post-processing streams request at once. The completion
+ * slot streamProcessingComplete() can only execute when we are out
+ * this critical section (result->streamsProcessMutex_). This helps to
+ * handle synchronous errors here itself.
*/
- uint64_t sensorTimestamp = static_cast<uint64_t>(request->metadata()
- .get(controls::SensorTimestamp)
- .value_or(0));
- notifyShutter(descriptor->frameNumber_, sensorTimestamp);
+ for (auto *buffer : camera3Result->pendingBuffersToProcess_) {
+ int ret = buffer->stream->process(buffer);
+ if (ret) {
+ setBufferStatus(*buffer, StreamBuffer::Status::Error);
+
+ /*
+ * If the framebuffer is internal to CameraStream return
+ * it back now that we're done processing it.
+ */
+ if (buffer->internalBuffer)
+ buffer->stream->putBuffer(buffer->internalBuffer);
- LOG(HAL, Debug) << "Request " << request->cookie() << " completed with "
- << descriptor->request_->buffers().size() << " streams";
+ LOG(HAL, Error) << "Failed to run post process of request " << descriptor->frameNumber_;
+ }
+ }
+
+ if (!camera3Result->pendingBuffersToProcess_.empty())
+ return;
+
+ locker.unlock();
+ completeResultDescriptor(camera3Result);
+}
+
+void CameraDevice::requestComplete(Request *request)
+{
+ Camera3RequestDescriptor *camera3Request =
+ reinterpret_cast<Camera3RequestDescriptor *>(request->cookie());
/*
- * Generate the metadata associated with the captured buffers.
+ * On Android, each new partial result with a metadata must set a
+ * field (partial_result) to a distinct inclusive value between
+ * 1 and ANDROID_REQUEST_PARTIAL_RESULT_COUNT and the final result with
+ * metadata has to set the field as ANDROID_REQUEST_PARTIAL_RESULT_COUNT.
+ *
+ * An empty metadata with ANDROID_REQUEST_PARTIAL_RESULT_COUNT is not
+ * allowed. Add a result with a fixed metadata on requestComplete()
+ * in case all of previous metadata are sent early, and no more metadata
+ * can be sent as the final with ANDROID_REQUEST_PARTIAL_RESULT_COUNT.
*
- * Notify if the metadata generation has failed, but continue processing
- * buffers and return an empty metadata pack.
+ * See comments in completeResultDescriptor().
*/
- descriptor->resultMetadata_ = getResultMetadata(*descriptor);
- if (!descriptor->resultMetadata_) {
- notifyError(descriptor->frameNumber_, nullptr, CAMERA3_MSG_ERROR_RESULT);
+ Camera3ResultDescriptor *result = new Camera3ResultDescriptor(camera3Request);
+ result->resultMetadata_ = getFixedResultMetadata(camera3Request->settings_);
- /*
- * The camera framework expects an empty metadata pack on error.
- *
- * \todo Check that the post-processor code handles this situation
- * correctly.
- */
- descriptor->resultMetadata_ = std::make_unique<CameraMetadata>(0, 0);
+ {
+ MutexLocker lock(camera3Request->resultsMutex_);
+
+ switch (request->status()) {
+ case Request::RequestComplete:
+ camera3Request->status_ = Camera3RequestDescriptor::Status::Success;
+ break;
+ case Request::RequestCancelled:
+ camera3Request->status_ = Camera3RequestDescriptor::Status::Cancelled;
+ break;
+ case Request::RequestPending:
+ LOG(HAL, Fatal) << "Try to complete an unfinished request";
+ break;
+ }
+
+ camera3Request->results_.emplace_back(result);
}
- /* Handle post-processing. */
- MutexLocker locker(descriptor->streamsProcessMutex_);
+ completeResultDescriptor(result);
+ return;
+}
+
+void CameraDevice::completeResultDescriptor(Camera3ResultDescriptor *result)
+{
+ Camera3RequestDescriptor *request = result->request_;
+ result->complete_ = true;
+
+ MutexLocker lock(request->resultsMutex_);
+
+ bool requestCompleted = (request->status_ != Camera3RequestDescriptor::Status::Pending);
+ bool requestCancelled = (request->status_ == Camera3RequestDescriptor::Status::Cancelled);
+
+ bool hasPendingMetadata = false;
+ bool hasPendingResult = false;
+
+ for (auto &r : request->results_) {
+ if (!r->complete_) {
+ hasPendingResult = true;
+ if (r->resultMetadata_)
+ hasPendingMetadata = true;
+ }
+ }
/*
- * Queue all the post-processing streams request at once. The completion
- * slot streamProcessingComplete() can only execute when we are out
- * this critical section. This helps to handle synchronous errors here
- * itself.
+ * Android requires value of metadataPackIndex follows the rules:
+ *
+ * Set to 0: Result contains no metadata (buffers only).
+ * Set between 1 and (MaxMetadataPackIndex - 1): Result contains metadata.
+ * Set to MaxMetadataPackIndex: The final result having metadata.
*/
- auto iter = descriptor->pendingStreamsToProcess_.begin();
- while (iter != descriptor->pendingStreamsToProcess_.end()) {
- CameraStream *stream = iter->first;
- StreamBuffer *buffer = iter->second;
+ uint32_t &metadataPackIndex = result->metadataPackIndex_ = 0;
+ if (result->resultMetadata_) {
+ metadataPackIndex = request->nextPartialResultIndex_++;
- if (stream->isJpegStream()) {
- generateJpegExifMetadata(descriptor, buffer);
+ if (requestCompleted && !hasPendingMetadata) {
+ metadataPackIndex = CameraCapabilities::MaxMetadataPackIndex;
}
-
- FrameBuffer *src = request->findBuffer(stream->stream());
- if (!src) {
- LOG(HAL, Error) << "Failed to find a source stream buffer";
- setBufferStatus(*buffer, StreamBuffer::Status::Error);
- iter = descriptor->pendingStreamsToProcess_.erase(iter);
- continue;
+ if (metadataPackIndex > CameraCapabilities::MaxMetadataPackIndex) {
+ LOG(HAL, Fatal) << "Partial result exceed limited count "
+ << CameraCapabilities::MaxMetadataPackIndex;
}
+ }
- buffer->srcBuffer = src;
+ lock.unlock();
- ++iter;
- int ret = stream->process(buffer);
- if (ret) {
- setBufferStatus(*buffer, StreamBuffer::Status::Error);
- descriptor->pendingStreamsToProcess_.erase(stream);
+ sendCaptureResult(result);
- /*
- * If the framebuffer is internal to CameraStream return
- * it back now that we're done processing it.
- */
- if (buffer->internalBuffer)
- stream->putBuffer(buffer->internalBuffer);
- }
+ /*
+ * Call notify with CAMERA3_MSG_ERROR_RESULT to indicate some of the
+ * expected result metadata might not be available for the capture. Only
+ * calls when all pending metadata are sent, since Android ignores
+ * the following metadata after the notice.
+ */
+ if (requestCancelled && !hasPendingMetadata) {
+ notifyError(request->frameNumber_, nullptr, CAMERA3_MSG_ERROR_RESULT);
}
- if (descriptor->pendingStreamsToProcess_.empty()) {
- locker.unlock();
- completeDescriptor(descriptor);
+ if (requestCompleted && !hasPendingResult) {
+ completeRequestDescriptor(request);
}
}
@@ -1260,91 +1389,99 @@ void CameraDevice::requestComplete(Request *request)
* \brief Complete the Camera3RequestDescriptor
* \param[in] descriptor The Camera3RequestDescriptor that has completed
*
- * The function marks the Camera3RequestDescriptor as 'complete'. It shall be
- * called when all the streams in the Camera3RequestDescriptor have completed
- * capture (or have been generated via post-processing) and the request is ready
- * to be sent back to the framework.
+ * The function shall be called when all the buffers and metadata in the
+ * Camera3RequestDescriptor have completed (or have been generated via
+ * post-processing) and all results are sent back to the framework.
*
* \context This function is \threadsafe.
*/
-void CameraDevice::completeDescriptor(Camera3RequestDescriptor *descriptor)
+void CameraDevice::completeRequestDescriptor(Camera3RequestDescriptor *descriptor)
{
MutexLocker lock(descriptorsMutex_);
- descriptor->complete_ = true;
- sendCaptureResults();
+ auto iter = find_if(descriptors_.begin(), descriptors_.end(),
+ [&descriptor](auto &item) {
+ return item.get() == descriptor;
+ });
+
+ if (iter == descriptors_.end()) {
+ LOG(HAL, Error) << "Complete an unrecognized request";
+ return;
+ }
+
+ descriptors_.erase(iter);
}
-/**
- * \brief Sequentially send capture results to the framework
- *
- * Iterate over the descriptors queue to send completed descriptors back to the
- * framework, in the same order as they have been queued. For each complete
- * descriptor, populate a locally-scoped camera3_capture_result_t from the
- * descriptor, send the capture result back by calling the
- * process_capture_result() callback, and remove the descriptor from the queue.
- * Stop iterating if the descriptor at the front of the queue is not complete.
- *
- * This function should never be called directly in the codebase. Use
- * completeDescriptor() instead.
- */
-void CameraDevice::sendCaptureResults()
+void CameraDevice::setBufferStatus(StreamBuffer &streamBuffer,
+ StreamBuffer::Status status) const
{
- while (!descriptors_.empty() && !descriptors_.front()->isPending()) {
- auto descriptor = std::move(descriptors_.front());
- descriptors_.pop();
+ streamBuffer.status = status;
+ if (status != StreamBuffer::Status::Success) {
+ notifyError(streamBuffer.request->frameNumber_,
+ streamBuffer.stream->camera3Stream(),
+ CAMERA3_MSG_ERROR_BUFFER);
+ }
+}
- camera3_capture_result_t captureResult = {};
+void CameraDevice::returnInternalBuffer(StreamBuffer &streamBuffer) const
+{
+ if (!streamBuffer.internalBuffer) {
+ return;
+ }
- captureResult.frame_number = descriptor->frameNumber_;
+ CameraStream *stream = streamBuffer.stream;
+ switch (stream->type()) {
+ case CameraStream::Type::Internal:
+ stream->putBuffer(streamBuffer.internalBuffer);
+ break;
+ case CameraStream::Type::Mapped:
+ /*
+ * The internal buffer of a mapped stream is allocated from
+ * its source stream
+ */
+ stream->sourceStream()->putBuffer(streamBuffer.internalBuffer);
+ break;
+ case CameraStream::Type::Direct:
+ LOG(HAL, Fatal) << "Direct stream buffer has internal buffer";
+ break;
+ }
+}
- if (descriptor->resultMetadata_)
- captureResult.result =
- descriptor->resultMetadata_->getMetadata();
+void CameraDevice::sendCaptureResult(Camera3ResultDescriptor *result) const
+{
+ camera3_capture_result_t captureResult = {};
- std::vector<camera3_stream_buffer_t> resultBuffers;
- resultBuffers.reserve(descriptor->buffers_.size());
+ captureResult.frame_number = result->request_->frameNumber_;
- for (auto &buffer : descriptor->buffers_) {
- camera3_buffer_status status = CAMERA3_BUFFER_STATUS_ERROR;
+ std::vector<camera3_stream_buffer_t> resultBuffers;
+ resultBuffers.reserve(result->buffers_.size());
- if (buffer.status == StreamBuffer::Status::Success)
- status = CAMERA3_BUFFER_STATUS_OK;
+ for (auto &buffer : result->buffers_) {
+ camera3_buffer_status status = CAMERA3_BUFFER_STATUS_ERROR;
- /*
- * Pass the buffer fence back to the camera framework as
- * a release fence. This instructs the framework to wait
- * on the acquire fence in case we haven't done so
- * ourselves for any reason.
- */
- resultBuffers.push_back({ buffer.stream->camera3Stream(),
- buffer.camera3Buffer, status,
- -1, buffer.fence.release() });
- }
+ if (buffer->status == StreamBuffer::Status::Success)
+ status = CAMERA3_BUFFER_STATUS_OK;
- captureResult.num_output_buffers = resultBuffers.size();
- captureResult.output_buffers = resultBuffers.data();
+ /*
+ * Pass the buffer fence back to the camera framework as
+ * a release fence. This instructs the framework to wait
+ * on the acquire fence in case we haven't done so
+ * ourselves for any reason.
+ */
+ resultBuffers.push_back({ buffer->stream->camera3Stream(),
+ buffer->camera3Buffer, status,
+ -1, buffer->fence.release() });
+ }
- if (descriptor->status_ == Camera3RequestDescriptor::Status::Success)
- captureResult.partial_result = 1;
+ captureResult.num_output_buffers = resultBuffers.size();
+ captureResult.output_buffers = resultBuffers.data();
- callbacks_->process_capture_result(callbacks_, &captureResult);
- }
-}
+ if (result->resultMetadata_)
+ captureResult.result = result->resultMetadata_->getMetadata();
-void CameraDevice::setBufferStatus(StreamBuffer &streamBuffer,
- StreamBuffer::Status status)
-{
- streamBuffer.status = status;
- if (status != StreamBuffer::Status::Success) {
- notifyError(streamBuffer.request->frameNumber_,
- streamBuffer.stream->camera3Stream(),
- CAMERA3_MSG_ERROR_BUFFER);
+ captureResult.partial_result = result->metadataPackIndex_;
- /* Also set error status on entire request descriptor. */
- streamBuffer.request->status_ =
- Camera3RequestDescriptor::Status::Error;
- }
+ callbacks_->process_capture_result(callbacks_, &captureResult);
}
/**
@@ -1354,11 +1491,11 @@ void CameraDevice::setBufferStatus(StreamBuffer &streamBuffer,
*
* This function is called from the post-processor's thread whenever a camera
* stream has finished post processing. The corresponding entry is dropped from
- * the descriptor's pendingStreamsToProcess_ map.
+ * the result's pendingBufferToProcess_ list.
*
- * If the pendingStreamsToProcess_ map is then empty, all streams requiring to
- * be generated from post-processing have been completed. Mark the descriptor as
- * complete using completeDescriptor() in that case.
+ * If the pendingBufferToProcess_ list is then empty, all streams requiring to
+ * be generated from post-processing have been completed. Mark the result as
+ * complete using completeResultDescriptor() in that case.
*/
void CameraDevice::streamProcessingComplete(StreamBuffer *streamBuffer,
StreamBuffer::Status status)
@@ -1370,19 +1507,18 @@ void CameraDevice::streamProcessingComplete(StreamBuffer *streamBuffer,
* that we're done processing it.
*/
if (streamBuffer->internalBuffer)
- streamBuffer->stream->putBuffer(streamBuffer->internalBuffer);
-
- Camera3RequestDescriptor *request = streamBuffer->request;
+ returnInternalBuffer(*streamBuffer);
+ Camera3ResultDescriptor *result = streamBuffer->result;
{
- MutexLocker locker(request->streamsProcessMutex_);
+ MutexLocker locker(result->streamsProcessMutex_);
+ result->pendingBuffersToProcess_.remove(streamBuffer);
- request->pendingStreamsToProcess_.erase(streamBuffer->stream);
- if (!request->pendingStreamsToProcess_.empty())
+ if (!result->pendingBuffersToProcess_.empty())
return;
}
- completeDescriptor(streamBuffer->request);
+ completeResultDescriptor(streamBuffer->result);
}
std::string CameraDevice::logPrefix() const
@@ -1414,6 +1550,99 @@ void CameraDevice::notifyError(uint32_t frameNumber, camera3_stream_t *stream,
callbacks_->notify(callbacks_, ¬ify);
}
+std::unique_ptr<CameraMetadata>
+CameraDevice::getDynamicResultMetadata(const ControlList &metadata) const
+{
+ /*
+ * \todo Keep this in sync with the actual number of entries.
+ *
+ * Reserve capacity for the metadata larger than 4 bytes which cannot
+ * store in entries.
+ * Currently: 6 entries, 40 bytes extra capaticy.
+ *
+ * ANDROID_SENSOR_TIMESTAMP (int64) = 8 bytes
+ * ANDROID_SENSOR_EXPOSURE_TIME (int64) = 8 bytes
+ * ANDROID_SENSOR_FRAME_DURATION (int64) = 8 bytes
+ * ANDROID_SCALER_CROP_REGION (int32 X 4) = 16 bytes
+ * Total bytes for capacity: 40
+ *
+ * Reserve more capacity for the JPEG metadata set by the post-processor.
+ * Currently: 8 entries, 72 bytes extra capaticy.
+ *
+ * ANDROID_JPEG_GPS_COORDINATES (double x 3) = 24 bytes
+ * ANDROID_JPEG_GPS_PROCESSING_METHOD (byte x 32) = 32 bytes
+ * ANDROID_JPEG_GPS_TIMESTAMP (int64) = 8 bytes
+ * ANDROID_JPEG_SIZE (int32_t) = 4 bytes
+ * ANDROID_JPEG_QUALITY (byte) = 1 byte
+ * ANDROID_JPEG_ORIENTATION (int32_t) = 4 bytes
+ * ANDROID_JPEG_THUMBNAIL_QUALITY (byte) = 1 byte
+ * ANDROID_JPEG_THUMBNAIL_SIZE (int32 x 2) = 8 bytes
+ * Total bytes for JPEG metadata: 72
+ *
+ * \todo Calculate the entries and capacity by the input ControlList.
+ */
+ std::unique_ptr<CameraMetadata> resultMetadata =
+ std::make_unique<CameraMetadata>(14, 112);
+ if (!resultMetadata->isValid()) {
+ LOG(HAL, Error) << "Failed to allocate result metadata";
+ return nullptr;
+ }
+
+ /* Add metadata tags reported by libcamera. */
+ const auto ×tamp = metadata.get(controls::SensorTimestamp);
+ if (timestamp)
+ resultMetadata->addEntry(ANDROID_SENSOR_TIMESTAMP, *timestamp);
+
+ const auto &pipelineDepth = metadata.get(controls::draft::PipelineDepth);
+ if (pipelineDepth)
+ resultMetadata->addEntry(ANDROID_REQUEST_PIPELINE_DEPTH,
+ *pipelineDepth);
+
+ const auto &exposureTime = metadata.get(controls::ExposureTime);
+ if (exposureTime)
+ resultMetadata->addEntry(ANDROID_SENSOR_EXPOSURE_TIME,
+ *exposureTime * 1000ULL);
+
+ const auto &frameDuration = metadata.get(controls::FrameDuration);
+ if (frameDuration)
+ resultMetadata->addEntry(ANDROID_SENSOR_FRAME_DURATION,
+ *frameDuration * 1000);
+
+ const auto &scalerCrop = metadata.get(controls::ScalerCrop);
+ if (scalerCrop) {
+ const Rectangle &crop = *scalerCrop;
+ int32_t cropRect[] = {
+ crop.x,
+ crop.y,
+ static_cast<int32_t>(crop.width),
+ static_cast<int32_t>(crop.height),
+ };
+ resultMetadata->addEntry(ANDROID_SCALER_CROP_REGION, cropRect);
+ }
+
+ const auto &testPatternMode = metadata.get(controls::draft::TestPatternMode);
+ if (testPatternMode)
+ resultMetadata->addEntry(ANDROID_SENSOR_TEST_PATTERN_MODE,
+ *testPatternMode);
+
+ /*
+ * Return the result metadata pack even is not valid: get() will return
+ * nullptr.
+ */
+ if (!resultMetadata->isValid()) {
+ LOG(HAL, Error) << "Failed to construct result metadata";
+ }
+
+ if (resultMetadata->resized()) {
+ auto [entryCount, dataCount] = resultMetadata->usage();
+ LOG(HAL, Info)
+ << "Result metadata resized: " << entryCount
+ << " entries and " << dataCount << " bytes used";
+ }
+
+ return resultMetadata;
+}
+
/*
* Set jpeg metadata used to generate EXIF in the JPEG post processing.
*/
@@ -1424,10 +1653,8 @@ void CameraDevice::generateJpegExifMetadata(Camera3RequestDescriptor *request,
auto &jpegExifMetadata = buffer->jpegExifMetadata;
jpegExifMetadata.emplace(StreamBuffer::JpegExifMetadata());
- jpegExifMetadata->sensorExposureTime = 0;
- if (metadata.contains(controls::ExposureTime)) {
- jpegExifMetadata->sensorExposureTime = metadata.get(controls::ExposureTime) * 1000ULL;
- }
+ const int64_t exposureTime = metadata.get(controls::ExposureTime).value_or(0);
+ jpegExifMetadata->sensorExposureTime = exposureTime;
/*
* todo: Android Sensitivity = analog gain X digital gain only on sensor.
@@ -1441,31 +1668,26 @@ void CameraDevice::generateJpegExifMetadata(Camera3RequestDescriptor *request,
* Produce a set of fixed result metadata.
*/
std::unique_ptr<CameraMetadata>
-CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) const
+CameraDevice::getFixedResultMetadata(const CameraMetadata &settings) const
{
- const ControlList &metadata = descriptor.request_->metadata();
- const CameraMetadata &settings = descriptor.settings_;
camera_metadata_ro_entry_t entry;
bool found;
/*
+ * \todo Retrieve metadata from corresponding libcamera controls.
* \todo Keep this in sync with the actual number of entries.
- * Currently: 40 entries, 156 bytes
*
- * Reserve more space for the JPEG metadata set by the post-processor.
- * Currently:
- * ANDROID_JPEG_GPS_COORDINATES (double x 3) = 24 bytes
- * ANDROID_JPEG_GPS_PROCESSING_METHOD (byte x 32) = 32 bytes
- * ANDROID_JPEG_GPS_TIMESTAMP (int64) = 8 bytes
- * ANDROID_JPEG_SIZE (int32_t) = 4 bytes
- * ANDROID_JPEG_QUALITY (byte) = 1 byte
- * ANDROID_JPEG_ORIENTATION (int32_t) = 4 bytes
- * ANDROID_JPEG_THUMBNAIL_QUALITY (byte) = 1 byte
- * ANDROID_JPEG_THUMBNAIL_SIZE (int32 x 2) = 8 bytes
- * Total bytes for JPEG metadata: 82
+ * Reserve capacity for the metadata larger than 4 bytes which cannot
+ * store in entries.
+ * Currently: 31 entries, 16 bytes
+ *
+ * ANDROID_CONTROL_AE_TARGET_FPS_RANGE (int32 X 2) = 8 bytes
+ * ANDROID_SENSOR_ROLLING_SHUTTER_SKEW (int64) = 8 bytes
+ *
+ * Total bytes: 16
*/
std::unique_ptr<CameraMetadata> resultMetadata =
- std::make_unique<CameraMetadata>(88, 166);
+ std::make_unique<CameraMetadata>(31, 16);
if (!resultMetadata->isValid()) {
LOG(HAL, Error) << "Failed to allocate result metadata";
return nullptr;
@@ -1563,9 +1785,6 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons
resultMetadata->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
value);
- value32 = ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
- resultMetadata->addEntry(ANDROID_SENSOR_TEST_PATTERN_MODE, value32);
-
value = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
resultMetadata->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, value);
@@ -1587,40 +1806,6 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons
resultMetadata->addEntry(ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
rolling_shutter_skew);
- /* Add metadata tags reported by libcamera. */
- const int64_t timestamp = metadata.get(controls::SensorTimestamp).value_or(0);
- resultMetadata->addEntry(ANDROID_SENSOR_TIMESTAMP, timestamp);
-
- const auto &pipelineDepth = metadata.get(controls::draft::PipelineDepth);
- if (pipelineDepth)
- resultMetadata->addEntry(ANDROID_REQUEST_PIPELINE_DEPTH,
- *pipelineDepth);
-
- const auto &exposureTime = metadata.get(controls::ExposureTime);
- if (exposureTime)
- resultMetadata->addEntry(ANDROID_SENSOR_EXPOSURE_TIME,
- *exposureTime * 1000ULL);
-
- const auto &frameDuration = metadata.get(controls::FrameDuration);
- if (frameDuration)
- resultMetadata->addEntry(ANDROID_SENSOR_FRAME_DURATION,
- *frameDuration * 1000);
-
- const auto &scalerCrop = metadata.get(controls::ScalerCrop);
- if (scalerCrop) {
- const Rectangle &crop = *scalerCrop;
- int32_t cropRect[] = {
- crop.x, crop.y, static_cast<int32_t>(crop.width),
- static_cast<int32_t>(crop.height),
- };
- resultMetadata->addEntry(ANDROID_SCALER_CROP_REGION, cropRect);
- }
-
- const auto &testPatternMode = metadata.get(controls::draft::TestPatternMode);
- if (testPatternMode)
- resultMetadata->addEntry(ANDROID_SENSOR_TEST_PATTERN_MODE,
- *testPatternMode);
-
/*
* Return the result metadata pack even is not valid: get() will return
* nullptr.
diff --git a/src/android/camera_device.h b/src/android/camera_device.h
index 7b279895..0c4237a6 100644
--- a/src/android/camera_device.h
+++ b/src/android/camera_device.h
@@ -20,6 +20,7 @@
#include <libcamera/base/mutex.h>
#include <libcamera/camera.h>
+#include <libcamera/controls.h>
#include <libcamera/framebuffer.h>
#include <libcamera/geometry.h>
#include <libcamera/pixel_format.h>
@@ -29,7 +30,6 @@
#include "camera_capabilities.h"
#include "camera_metadata.h"
#include "camera_stream.h"
-#include "jpeg/encoder.h"
class Camera3RequestDescriptor;
struct CameraConfigData;
@@ -63,6 +63,8 @@ public:
const camera_metadata_t *constructDefaultRequestSettings(int type);
int configureStreams(camera3_stream_configuration_t *stream_list);
int processCaptureRequest(camera3_capture_request_t *request);
+ void partialResultComplete(libcamera::Request *request,
+ libcamera::Result *result);
void requestComplete(libcamera::Request *request);
void streamProcessingComplete(StreamBuffer *bufferStream,
StreamBuffer::Status status);
@@ -87,20 +89,25 @@ private:
createFrameBuffer(const buffer_handle_t camera3buffer,
libcamera::PixelFormat pixelFormat,
const libcamera::Size &size);
- void abortRequest(Camera3RequestDescriptor *descriptor) const;
bool isValidRequest(camera3_capture_request_t *request) const;
void notifyShutter(uint32_t frameNumber, uint64_t timestamp);
void notifyError(uint32_t frameNumber, camera3_stream_t *stream,
camera3_error_msg_code code) const;
int processControls(Camera3RequestDescriptor *descriptor);
- void completeDescriptor(Camera3RequestDescriptor *descriptor)
+ void abortRequest(Camera3RequestDescriptor *descriptor);
+ void completeResultDescriptor(Camera3ResultDescriptor *result);
+ void completeRequestDescriptor(Camera3RequestDescriptor *descriptor)
LIBCAMERA_TSA_EXCLUDES(descriptorsMutex_);
- void sendCaptureResults() LIBCAMERA_TSA_REQUIRES(descriptorsMutex_);
- void setBufferStatus(StreamBuffer &buffer, StreamBuffer::Status status);
+ void sendCaptureResult(Camera3ResultDescriptor *partialResult) const;
+ void setBufferStatus(StreamBuffer &buffer, StreamBuffer::Status status) const;
+ void returnInternalBuffer(StreamBuffer &buffer) const;
void generateJpegExifMetadata(Camera3RequestDescriptor *request,
StreamBuffer *buffer) const;
- std::unique_ptr<CameraMetadata> getResultMetadata(
- const Camera3RequestDescriptor &descriptor) const;
+
+ std::unique_ptr<CameraMetadata> getDynamicResultMetadata(
+ const libcamera::ControlList &metadata) const;
+ std::unique_ptr<CameraMetadata> getFixedResultMetadata(
+ const CameraMetadata &settings) const;
unsigned int id_;
camera3_device_t camera3Device_;
@@ -118,7 +125,7 @@ private:
std::vector<CameraStream> streams_;
libcamera::Mutex descriptorsMutex_ LIBCAMERA_TSA_ACQUIRED_AFTER(stateMutex_);
- std::queue<std::unique_ptr<Camera3RequestDescriptor>> descriptors_
+ std::list<std::unique_ptr<Camera3RequestDescriptor>> descriptors_
LIBCAMERA_TSA_GUARDED_BY(descriptorsMutex_);
std::string maker_;
diff --git a/src/android/camera_request.cpp b/src/android/camera_request.cpp
index f5d4d314..31751b5c 100644
--- a/src/android/camera_request.cpp
+++ b/src/android/camera_request.cpp
@@ -42,25 +42,25 @@ using namespace libcamera;
* │ processCaptureRequest(camera3_capture_request_t request) │
* │ │
* │ - Create Camera3RequestDescriptor tracking this request │
- * │ - Streams requiring post-processing are stored in the │
- * │ pendingStreamsToProcess map │
+ * │ - Buffer requiring post-processing are marked by the │
+ * │ CameraStream::Type as Mapped or Internal │
* │ - Add this Camera3RequestDescriptor to descriptors' queue │
* │ CameraDevice::descriptors_ │
- * │ │ ┌─────────────────────────┐
- * │ - Queue the capture request to libcamera core ────────────┤►│libcamera core │
- * │ │ ├─────────────────────────┤
- * │ │ │- Capture from Camera │
- * │ │ │ │
- * │ │ │- Emit │
- * │ │ │ Camera::requestComplete│
- * │ requestCompleted(Request *request) ◄───────────────────────┼─┼──── │
- * │ │ │ │
- * │ - Check request completion status │ └─────────────────────────┘
+ * │ │ ┌───────────────────────────────┐
+ * │ - Queue the capture request to libcamera core ────────────┤►│libcamera core │
+ * │ │ ├───────────────────────────────┤
+ * │ │ │- Capture from Camera │
+ * │ │ │ │
+ * │ │ │- Emit │
+ * │ │ │ Camera::partialResultComplete│
+ * │ partialResultComplete(Request *request, Result result*) ◄──┼─┼──── │
+ * │ │ │ │
+ * │ - Check request completion status │ └───────────────────────────────┘
* │ │
- * │ - if (pendingStreamsToProcess > 0) │
- * │ Queue all entries from pendingStreamsToProcess │
+ * │ - if (pendingBuffersToProcess > 0) │
+ * │ Queue all entries from pendingBuffersToProcess │
* │ else │ │
- * │ completeDescriptor() │ └──────────────────────┐
+ * │ completeResultDescriptor() │ └──────────────────────┐
* │ │ │
* │ ┌──────────────────────────┴───┬──────────────────┐ │
* │ │ │ │ │
@@ -93,10 +93,10 @@ using namespace libcamera;
* │ | | | | │
* │ | - Check and set buffer status | | .... | │
* │ | - Remove post+processing entry | | | │
- * │ | from pendingStreamsToProcess | | | │
+ * │ | from pendingBuffersToProcess | | | │
* │ | | | | │
- * │ | - if (pendingStreamsToProcess.empty())| | | │
- * │ | completeDescriptor | | | │
+ * │ | - if (pendingBuffersToProcess.empty())| | | │
+ * │ | completeResultDescriptor | | | │
* │ | | | | │
* │ +---------------------------------------+ +--------------+ │
* │ │
@@ -110,6 +110,7 @@ using namespace libcamera;
Camera3RequestDescriptor::Camera3RequestDescriptor(
Camera *camera, const camera3_capture_request_t *camera3Request)
+ : status_(Status::Pending), nextPartialResultIndex_(1)
{
frameNumber_ = camera3Request->frame_number;
@@ -140,6 +141,19 @@ Camera3RequestDescriptor::Camera3RequestDescriptor(
Camera3RequestDescriptor::~Camera3RequestDescriptor() = default;
+/*
+ * \class Camera3ResultDescriptor
+ *
+ * A utility class that groups information about a capture result to be later
+ * sent to framework.
+ */
+Camera3ResultDescriptor::Camera3ResultDescriptor(Camera3RequestDescriptor *request)
+ : complete_(false), request_(request), metadataPackIndex_(1)
+{
+}
+
+Camera3ResultDescriptor::~Camera3ResultDescriptor() = default;
+
/**
* \class StreamBuffer
* \brief Group information for per-stream buffer of Camera3RequestDescriptor
diff --git a/src/android/camera_request.h b/src/android/camera_request.h
index f91de955..b5d9e3ee 100644
--- a/src/android/camera_request.h
+++ b/src/android/camera_request.h
@@ -24,6 +24,7 @@
class CameraBuffer;
class CameraStream;
+class Camera3ResultDescriptor;
class Camera3RequestDescriptor;
class StreamBuffer
@@ -56,41 +57,64 @@ public:
const libcamera::FrameBuffer *srcBuffer = nullptr;
std::unique_ptr<CameraBuffer> dstBuffer;
std::optional<JpegExifMetadata> jpegExifMetadata;
+ Camera3ResultDescriptor *result;
Camera3RequestDescriptor *request;
private:
LIBCAMERA_DISABLE_COPY(StreamBuffer)
};
+class Camera3ResultDescriptor
+{
+public:
+ Camera3ResultDescriptor(Camera3RequestDescriptor *request);
+ ~Camera3ResultDescriptor();
+
+ bool complete_;
+ Camera3RequestDescriptor *request_;
+ uint32_t metadataPackIndex_;
+
+ std::unique_ptr<CameraMetadata> resultMetadata_;
+ std::vector<StreamBuffer *> buffers_;
+
+ /* Keeps track of buffers waiting for post-processing. */
+ std::list<StreamBuffer *> pendingBuffersToProcess_
+ LIBCAMERA_TSA_GUARDED_BY(streamsProcessMutex_);
+ libcamera::Mutex streamsProcessMutex_;
+
+private:
+ LIBCAMERA_DISABLE_COPY(Camera3ResultDescriptor)
+};
+
class Camera3RequestDescriptor
{
public:
enum class Status {
+ Pending,
Success,
- Error,
+ Cancelled,
+ Flushed,
};
- /* Keeps track of streams requiring post-processing. */
- std::map<CameraStream *, StreamBuffer *> pendingStreamsToProcess_
- LIBCAMERA_TSA_GUARDED_BY(streamsProcessMutex_);
- libcamera::Mutex streamsProcessMutex_;
-
Camera3RequestDescriptor(libcamera::Camera *camera,
const camera3_capture_request_t *camera3Request);
~Camera3RequestDescriptor();
- bool isPending() const { return !complete_; }
-
uint32_t frameNumber_ = 0;
std::vector<StreamBuffer> buffers_;
CameraMetadata settings_;
std::unique_ptr<libcamera::Request> request_;
- std::unique_ptr<CameraMetadata> resultMetadata_;
- bool complete_ = false;
- Status status_ = Status::Success;
+ std::vector<std::unique_ptr<Camera3ResultDescriptor>> results_
+ LIBCAMERA_TSA_GUARDED_BY(resultsMutex_);
+
+ Status status_ LIBCAMERA_TSA_GUARDED_BY(resultsMutex_);
+
+ uint32_t nextPartialResultIndex_ LIBCAMERA_TSA_GUARDED_BY(resultsMutex_);
+
+ libcamera::Mutex resultsMutex_;
private:
LIBCAMERA_DISABLE_COPY(Camera3RequestDescriptor)
diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp
index 91fd329c..0cb5fd9a 100644
--- a/src/android/jpeg/post_processor_jpeg.cpp
+++ b/src/android/jpeg/post_processor_jpeg.cpp
@@ -111,7 +111,7 @@ void PostProcessorJpeg::process(StreamBuffer *streamBuffer)
ASSERT(jpegExifMetadata.has_value());
const CameraMetadata &requestMetadata = streamBuffer->request->settings_;
- CameraMetadata *resultMetadata = streamBuffer->request->resultMetadata_.get();
+ CameraMetadata *resultMetadata = streamBuffer->result->resultMetadata_.get();
camera_metadata_ro_entry_t entry;
int ret;
--
2.37.1.359.gd136c6c3e2-goog
More information about the libcamera-devel
mailing list