Show a patch.

GET /api/1.1/patches/17092/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 17092,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/17092/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/17092/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20220812090838.1784703-7-hanlinchen@chromium.org>",
    "date": "2022-08-12T09:08:37",
    "name": "[libcamera-devel,v2,6/7] android: Implement partial result feature based on partialResultCompleted signal",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "2965cd41ce4d5869296233d55005bd43b504ea1c",
    "submitter": {
        "id": 98,
        "url": "https://patchwork.libcamera.org/api/1.1/people/98/?format=api",
        "name": "Hanlin Chen",
        "email": "hanlinchen@chromium.org"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/17092/mbox/",
    "series": [
        {
            "id": 3408,
            "url": "https://patchwork.libcamera.org/api/1.1/series/3408/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3408",
            "date": "2022-08-12T09:08:31",
            "name": "Implement Android Partial Result Featrue",
            "version": 2,
            "mbox": "https://patchwork.libcamera.org/series/3408/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/17092/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/17092/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id D9362C3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 12 Aug 2022 09:09:04 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A001463332;\n\tFri, 12 Aug 2022 11:09:04 +0200 (CEST)",
            "from mail-pf1-x42c.google.com (mail-pf1-x42c.google.com\n\t[IPv6:2607:f8b0:4864:20::42c])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 403CA63326\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 12 Aug 2022 11:09:02 +0200 (CEST)",
            "by mail-pf1-x42c.google.com with SMTP id g12so434048pfb.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 12 Aug 2022 02:09:02 -0700 (PDT)",
            "from localhost ([2401:fa00:1:17:1705:d284:d114:2e24])\n\tby smtp.gmail.com with UTF8SMTPSA id\n\tj15-20020a170902da8f00b0016f04c098ddsm1166674plx.226.2022.08.12.02.08.58\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tFri, 12 Aug 2022 02:08:59 -0700 (PDT)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1660295344;\n\tbh=XUVXIH095CW+orozHNb+edNGl6eV+3R8nVMFXij+Gb8=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=mGZh/sPPLbEQ7pxUxL8JvF9siIFMmND9E4/7+2yH2MkALp/10TxkGjA6rXIJGOsDt\n\tQ6Tq6g2TOLwPMxdSlW/DYGkgiyZZrJEZtp+2m+Gwz92g+6iRRjGxqwTy+ubx9jQaV/\n\tOgEtws1V/j/zsGGoN97m4+aKomUKCmkOB0/pK3rVJAzu505ZAQWT0/U20E1aJr9LAq\n\tw1tz/f1UZrBMyNByUXdAhm/JaRR4EAWrY8xLy3u4BycMIqK1J5KuKBzd5SblM2iVxv\n\tODLz/UQHwuWqQpxsGTQRM6LMNxkZIFA8yGnmc1xnr47c8jt1W3AZFs3dPgdl4DeKMl\n\tcJ0dIXhkVq5sg==",
            "v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc;\n\tbh=c+5pmxEp18nuPmAb1DA+iCZFZ8j9iB6RgdwRFw1tOxU=;\n\tb=UWaX9zKVm/F9ghKdmZmjn80z8CNPER5ptR7VQLeNSgnMiKm2Sllieb2OgxAsB0Z5fk\n\thqtt5qPiaNR0ibLxyJt14KANmhKUJn/vJFtJQ96g8k6/VO+HekNETu14LrNY9Gqf1Ja2\n\tmHI72D3KtKE1Qs5/aUh3qZ1GqdBmRJwlTIc/c="
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=chromium.org\n\theader.i=@chromium.org header.b=\"UWaX9zKV\"; \n\tdkim-atps=neutral",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc;\n\tbh=c+5pmxEp18nuPmAb1DA+iCZFZ8j9iB6RgdwRFw1tOxU=;\n\tb=gvydsM0ET5gI7zaaypLLEGmnyzM/j8zCz6S4ke5VhOAjqHdKjui9bcVoQZ77JFUfyz\n\tZXYnoszM6cwt+YHH2fZMhSAfFBGAyMmrOZyBOGOiq64DFrFRXSZj9wKhZTcz4/pPJOTF\n\tEaQbp/9zycHKlaDEZuhWY3hhS7+yEHgvPX4QgVgiXmGhxtejRvBcndbKEOpXqLSiMId5\n\tN1mqhvPns323SBl7QFezF8e7zMNkvU1fMBo4Qwjx+argtXIGLXsUijGudA5l+pKLByZ5\n\tmw1Tlepbm4XFyqWGMUDa8f/VdwYlE7FkMqmBVQWfuk7es2kkDke7ASidu70mgz/pyCl6\n\tCyrg==",
        "X-Gm-Message-State": "ACgBeo3vDKhHWGIPwrU/kBDCUi0X+KULyHMQ7By0CZATlEqKGICARaD6\n\ttWGsr/fHL6PDOGSRAQ5LfAuODpxfzJR7nQ==",
        "X-Google-Smtp-Source": "AA6agR6EC+CiGbhtOkqTui6TkgUp4/uLUk2GSE9bFI21lzRBGmC4IFNsKHKc89sisfH4KQS8t0F17g==",
        "X-Received": "by 2002:a05:6a00:23d1:b0:52f:39e9:9150 with SMTP id\n\tg17-20020a056a0023d100b0052f39e99150mr3130866pfc.16.1660295339471; \n\tFri, 12 Aug 2022 02:08:59 -0700 (PDT)",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Fri, 12 Aug 2022 17:08:37 +0800",
        "Message-Id": "<20220812090838.1784703-7-hanlinchen@chromium.org>",
        "X-Mailer": "git-send-email 2.37.1.595.g718a3a8f04-goog",
        "In-Reply-To": "<20220812090838.1784703-1-hanlinchen@chromium.org>",
        "References": "<20220812090838.1784703-1-hanlinchen@chromium.org>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH v2 6/7] android: Implement partial result\n\tfeature based on partialResultCompleted signal",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "From": "Han-Lin Chen via libcamera-devel <libcamera-devel@lists.libcamera.org>",
        "Reply-To": "Han-Lin Chen <hanlinchen@chromium.org>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "To impelement partial result feature. The patch includes the following:\n1. Return partial results on Camera::partialResultCompleted signal, and recycle\nrequests on Camera::requestCompleted.\n2. Reorder the result according to the FIFO restriction of the same stream\n\nSigned-off-by: Han-Lin Chen <hanlinchen@chromium.org>\n---\n src/android/camera_capabilities.cpp      |   2 +-\n src/android/camera_capabilities.h        |   2 +\n src/android/camera_device.cpp            | 848 +++++++++++++++--------\n src/android/camera_device.h              |  36 +-\n src/android/camera_request.cpp           |  61 +-\n src/android/camera_request.h             |  56 +-\n src/android/camera_stream.cpp            |   1 +\n src/android/jpeg/post_processor_jpeg.cpp |   2 +-\n 8 files changed, 693 insertions(+), 315 deletions(-)",
    "diff": "diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp\nindex 64bd8dde..6ed040d0 100644\n--- a/src/android/camera_capabilities.cpp\n+++ b/src/android/camera_capabilities.cpp\n@@ -1374,7 +1374,7 @@ int CameraCapabilities::initializeStaticMetadata()\n \tstaticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);\n \n \t/* Request static metadata. */\n-\tint32_t partialResultCount = 1;\n+\tint32_t partialResultCount = MaxMetadataPackIndex;\n \tstaticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,\n \t\t\t\t  partialResultCount);\n \ndiff --git a/src/android/camera_capabilities.h b/src/android/camera_capabilities.h\nindex 6f66f221..b8fc20f1 100644\n--- a/src/android/camera_capabilities.h\n+++ b/src/android/camera_capabilities.h\n@@ -23,6 +23,8 @@\n class CameraCapabilities\n {\n public:\n+\tstatic constexpr int32_t MaxMetadataPackIndex = 64;\n+\n \tCameraCapabilities() = default;\n \n \tint initialize(std::shared_ptr<libcamera::Camera> camera,\ndiff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex a14d5de9..c999b198 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -24,9 +24,8 @@\n #include <libcamera/formats.h>\n #include <libcamera/property_ids.h>\n \n-#include \"system/graphics.h\"\n-\n #include \"camera_buffer.h\"\n+#include \"camera_capabilities.h\"\n #include \"camera_hal_config.h\"\n #include \"camera_ops.h\"\n #include \"camera_request.h\"\n@@ -249,7 +248,11 @@ CameraDevice::CameraDevice(unsigned int id, std::shared_ptr<Camera> camera)\n \t: id_(id), state_(State::Stopped), camera_(std::move(camera)),\n \t  facing_(CAMERA_FACING_FRONT), orientation_(0)\n {\n+\t/* Set RequestCompletionMode to Immediately to send result early */\n+\tcamera_->setRequestCompletionMode(Camera::Immediately);\n+\n \tcamera_->requestCompleted.connect(this, &CameraDevice::requestComplete);\n+\tcamera_->partialResultCompleted.connect(this, &CameraDevice::partialResultComplete);\n \n \tmaker_ = \"libcamera\";\n \tmodel_ = \"cameraModel\";\n@@ -438,8 +441,10 @@ void CameraDevice::stop()\n \tcamera_->stop();\n \n \t{\n-\t\tMutexLocker descriptorsLock(descriptorsMutex_);\n-\t\tdescriptors_ = {};\n+\t\tMutexLocker descriptorsLock(pendingRequestMutex_);\n+\t\tpendingRequests_.clear();\n+\t\tpendingResults_.clear();\n+\t\tpendingStreamBuffers_.clear();\n \t}\n \n \tstreams_.clear();\n@@ -855,14 +860,37 @@ int CameraDevice::processControls(Camera3RequestDescriptor *descriptor)\n \treturn 0;\n }\n \n-void CameraDevice::abortRequest(Camera3RequestDescriptor *descriptor) const\n+void CameraDevice::abortRequest(Camera3RequestDescriptor *descriptor)\n {\n-\tnotifyError(descriptor->frameNumber_, nullptr, CAMERA3_MSG_ERROR_REQUEST);\n+\t/*\n+\t * Since the failed buffers do not have to follow the strict ordering\n+\t * valid buffers do, and could be out-of-order with respect to valid\n+\t * buffers, it's safe to send the aborted result back to the framework\n+\t * direcly.\n+\t */\n+\tCamera3ResultDescriptor *result = new Camera3ResultDescriptor(descriptor);\n+\tresult->metadataPackIndex_ = 0;\n \n-\tfor (auto &buffer : descriptor->buffers_)\n-\t\tbuffer.status = StreamBuffer::Status::Error;\n+\tfor (auto &buffer : descriptor->buffers_) {\n+\t\tsetBufferStatus(buffer, StreamBuffer::Status::Error);\n+\t\tresult->buffers_.emplace_back(&buffer);\n+\t}\n+\n+\t{\n+\t\tMutexLocker lock(descriptor->resultsMutex_);\n+\t\tdescriptor->status_ = Camera3RequestDescriptor::Status::Aborted;\n+\t\tdescriptor->finalResult_.reset(result);\n+\t}\n \n-\tdescriptor->status_ = Camera3RequestDescriptor::Status::Error;\n+\t/*\n+\t * After CAMERA3_MSG_ERROR_REQUEST is notified, for a given frame,\n+\t * only process_capture_results with buffers of the status\n+\t * CAMERA3_BUFFER_STATUS_ERROR are allowed. No further notifys or\n+\t * process_capture_result with non-null metadata is allowed.\n+\t */\n+\tnotifyError(descriptor->frameNumber_, nullptr, CAMERA3_MSG_ERROR_REQUEST);\n+\n+\tsendCaptureResult(result);\n }\n \n bool CameraDevice::isValidRequest(camera3_capture_request_t *camera3Request) const\n@@ -962,9 +990,10 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n \t * to a libcamera stream. Streams of type Mapped will be handled later.\n \t *\n \t * Collect the CameraStream associated to each requested capture stream.\n-\t * Since requestedStreams is an std:set<>, no duplications can happen.\n+\t * Since requestedDirectBuffers is an std:map<>, no duplications can\n+\t * happen.\n \t */\n-\tstd::set<CameraStream *> requestedStreams;\n+\tstd::map<CameraStream *, libcamera::FrameBuffer *> requestedDirectBuffers;\n \tfor (const auto &[i, buffer] : utils::enumerate(descriptor->buffers_)) {\n \t\tCameraStream *cameraStream = buffer.stream;\n \t\tcamera3_stream_t *camera3Stream = cameraStream->camera3Stream();\n@@ -983,8 +1012,6 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n \t\tFrameBuffer *frameBuffer = nullptr;\n \t\tUniqueFD acquireFence;\n \n-\t\tMutexLocker lock(descriptor->streamsProcessMutex_);\n-\n \t\tswitch (cameraStream->type()) {\n \t\tcase CameraStream::Type::Mapped:\n \t\t\t/* Mapped streams will be handled in the next loop. */\n@@ -1003,23 +1030,23 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n \t\t\t\t\t\t  cameraStream->configuration().size);\n \t\t\tframeBuffer = buffer.frameBuffer.get();\n \t\t\tacquireFence = std::move(buffer.fence);\n+\n+\t\t\trequestedDirectBuffers[cameraStream] = frameBuffer;\n \t\t\tLOG(HAL, Debug) << ss.str() << \" (direct)\";\n \t\t\tbreak;\n \n \t\tcase CameraStream::Type::Internal:\n \t\t\t/*\n-\t\t\t * Get the frame buffer from the CameraStream internal\n-\t\t\t * buffer pool.\n-\t\t\t *\n-\t\t\t * The buffer has to be returned to the CameraStream\n-\t\t\t * once it has been processed.\n+\t\t\t * Get the frame buffer from the source stream's\n+\t\t\t * internal buffer pool. The buffer has to be returned\n+\t\t\t * to the source stream once it has been processed.\n \t\t\t */\n \t\t\tframeBuffer = cameraStream->getBuffer();\n-\t\t\tbuffer.internalBuffer = frameBuffer;\n-\t\t\tLOG(HAL, Debug) << ss.str() << \" (internal)\";\n+\t\t\tbuffer.srcBuffer = frameBuffer;\n \n-\t\t\tdescriptor->pendingStreamsToProcess_.insert(\n-\t\t\t\t{ cameraStream, &buffer });\n+\t\t\t/* Track the allocated internal buffer */\n+\t\t\tdescriptor->internalBuffers_[cameraStream] = frameBuffer;\n+\t\t\tLOG(HAL, Debug) << ss.str() << \" (internal)\";\n \t\t\tbreak;\n \t\t}\n \n@@ -1031,8 +1058,6 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n \t\tauto fence = std::make_unique<Fence>(std::move(acquireFence));\n \t\tdescriptor->request_->addBuffer(cameraStream->stream(),\n \t\t\t\t\t\tframeBuffer, std::move(fence));\n-\n-\t\trequestedStreams.insert(cameraStream);\n \t}\n \n \t/*\n@@ -1054,29 +1079,54 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n \t\t\t\t<< cameraStream->configuration().pixelFormat << \"]\"\n \t\t\t\t<< \" (mapped)\";\n \n-\t\tMutexLocker lock(descriptor->streamsProcessMutex_);\n-\t\tdescriptor->pendingStreamsToProcess_.insert({ cameraStream, &buffer });\n-\n \t\t/*\n \t\t * Make sure the CameraStream this stream is mapped on has been\n \t\t * added to the request.\n \t\t */\n \t\tCameraStream *sourceStream = cameraStream->sourceStream();\n+\n \t\tASSERT(sourceStream);\n-\t\tif (requestedStreams.find(sourceStream) != requestedStreams.end())\n+\t\tASSERT(sourceStream->type() == CameraStream::Type::Direct);\n+\n+\t\t/*\n+\t\t * If the buffer for the source stream has been requested as\n+\t\t * Direct, use its framebuffer as the source buffer for\n+\t\t * post-processing. No need to recycle the buffer since it's\n+\t\t * owned by Android.\n+\t\t */\n+\t\tauto iterDirectBuffer = requestedDirectBuffers.find(sourceStream);\n+\t\tif (iterDirectBuffer != requestedDirectBuffers.end()) {\n+\t\t\tbuffer.srcBuffer = iterDirectBuffer->second;\n \t\t\tcontinue;\n+\t\t}\n \n \t\t/*\n-\t\t * If that's not the case, we need to add a buffer to the request\n-\t\t * for this stream.\n+\t\t * If that's not the case, we use an internal buffer allocated\n+\t\t * from the source stream.\n+\t\t *\n+\t\t * If an internal buffer has been requested for the source\n+\t\t * stream before, we should reuse it.\n \t\t */\n-\t\tFrameBuffer *frameBuffer = cameraStream->getBuffer();\n-\t\tbuffer.internalBuffer = frameBuffer;\n+\t\tauto iterInternalBuffer = descriptor->internalBuffers_.find(sourceStream);\n+\t\tif (iterInternalBuffer != descriptor->internalBuffers_.end()) {\n+\t\t\tbuffer.srcBuffer = iterInternalBuffer->second;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/*\n+\t\t * Otherwise, we need to create an internal buffer to the\n+\t\t * request for the source stream. Get the frame buffer from the\n+\t\t * source stream's internal buffer pool. The buffer has to be\n+\t\t * returned to the source stream once it has been processed.\n+\t\t */\n+\t\tFrameBuffer *frameBuffer = sourceStream->getBuffer();\n+\t\tbuffer.srcBuffer = frameBuffer;\n \n \t\tdescriptor->request_->addBuffer(sourceStream->stream(),\n \t\t\t\t\t\tframeBuffer, nullptr);\n \n-\t\trequestedStreams.erase(sourceStream);\n+\t\t/* Track the allocated internal buffer. */\n+\t\tdescriptor->internalBuffers_[sourceStream] = frameBuffer;\n \t}\n \n \t/*\n@@ -1095,14 +1145,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n \tMutexLocker stateLock(stateMutex_);\n \n \tif (state_ == State::Flushing) {\n-\t\tCamera3RequestDescriptor *rawDescriptor = descriptor.get();\n-\t\t{\n-\t\t\tMutexLocker descriptorsLock(descriptorsMutex_);\n-\t\t\tdescriptors_.push(std::move(descriptor));\n-\t\t}\n-\t\tabortRequest(rawDescriptor);\n-\t\tcompleteDescriptor(rawDescriptor);\n-\n+\t\tabortRequest(descriptor.get());\n \t\treturn 0;\n \t}\n \n@@ -1119,8 +1162,10 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n \tRequest *request = descriptor->request_.get();\n \n \t{\n-\t\tMutexLocker descriptorsLock(descriptorsMutex_);\n-\t\tdescriptors_.push(std::move(descriptor));\n+\t\tMutexLocker descriptorsLock(pendingRequestMutex_);\n+\t\tfor (auto &buffer : descriptor->buffers_)\n+\t\t\tpendingStreamBuffers_[buffer.stream].push_back(&buffer);\n+\t\tpendingRequests_.push_back(std::move(descriptor));\n \t}\n \n \tcamera_->queueRequest(request);\n@@ -1128,223 +1173,444 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n \treturn 0;\n }\n \n-void CameraDevice::requestComplete(Request *request)\n+void CameraDevice::partialResultComplete(Request *request, Result *result)\n {\n \tCamera3RequestDescriptor *descriptor =\n \t\treinterpret_cast<Camera3RequestDescriptor *>(request->cookie());\n \n-\t/*\n-\t * Prepare the capture result for the Android camera stack.\n-\t *\n-\t * The buffer status is set to Success and later changed to Error if\n-\t * post-processing/compression fails.\n-\t */\n+\tif (result->buffers().empty() && result->metadata().empty())\n+\t\tLOG(HAL, Fatal)\n+\t\t\t<< \"Empty partial result is not allowed. Frame number: \"\n+\t\t\t<< descriptor->frameNumber_;\n+\n+\tCamera3ResultDescriptor *camera3Result = new Camera3ResultDescriptor(descriptor);\n+\n+\tconst ControlList &metadata = result->metadata();\n+\tif (!metadata.empty()) {\n+\t\t/*\n+\t\t * Notify shutter as soon as we have received SensorTimestamp.\n+\t\t */\n+\t\tconst auto &timestamp = metadata.get(controls::SensorTimestamp);\n+\t\tif (timestamp) {\n+\t\t\tnotifyShutter(descriptor->frameNumber_, *timestamp);\n+\t\t\tLOG(HAL, Debug) << \"Request \" << request->cookie() << \" notifies shutter\";\n+\t\t}\n+\n+\t\tcamera3Result->resultMetadata_ = getDynamicResultMetadata(metadata);\n+\t}\n+\n+\tMutexLocker locker(camera3Result->streamsProcessMutex_);\n+\n \tfor (auto &buffer : descriptor->buffers_) {\n-\t\tCameraStream *stream = buffer.stream;\n+\t\tCameraStream *cameraStream = buffer.stream;\n+\t\tfor (auto *frameBuffer : result->buffers()) {\n+\t\t\tif (buffer.srcBuffer != frameBuffer &&\n+\t\t\t    buffer.frameBuffer.get() != frameBuffer)\n+\t\t\t\tcontinue;\n+\n+\t\t\tbuffer.result = camera3Result;\n+\t\t\tcamera3Result->buffers_.emplace_back(&buffer);\n \n+\t\t\tStreamBuffer::Status status = StreamBuffer::Status::Success;\n+\t\t\tif (frameBuffer->metadata().status != FrameMetadata::FrameSuccess) {\n+\t\t\t\tstatus = StreamBuffer::Status::Error;\n+\t\t\t}\n+\t\t\tsetBufferStatus(buffer, status);\n+\n+\t\t\tswitch (cameraStream->type()) {\n+\t\t\tcase CameraStream::Type::Direct: {\n+\t\t\t\tASSERT(buffer.frameBuffer.get() == frameBuffer);\n+\t\t\t\t/*\n+\t\t\t\t * Streams of type Direct have been queued to the\n+\t\t\t\t * libcamera::Camera and their acquire fences have\n+\t\t\t\t * already been waited on by the library.\n+\t\t\t\t */\n+\t\t\t\tstd::unique_ptr<Fence> fence = buffer.frameBuffer->releaseFence();\n+\t\t\t\tif (fence)\n+\t\t\t\t\tbuffer.fence = fence->release();\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tcase CameraStream::Type::Mapped:\n+\t\t\tcase CameraStream::Type::Internal:\n+\t\t\t\tASSERT(buffer.srcBuffer == frameBuffer);\n+\t\t\t\tif (status == StreamBuffer::Status::Error)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\t/*\n+\t\t\t\t * Acquire fences of streams of type Internal and Mapped\n+\t\t\t\t * will be handled during post-processing.\n+\t\t\t\t */\n+\t\t\t\tbuffer.srcBuffer = frameBuffer;\n+\t\t\t\tcamera3Result->pendingBuffersToProcess_.emplace_back(&buffer);\n+\n+\t\t\t\tif (cameraStream->isJpegStream()) {\n+\t\t\t\t\tgenerateJpegExifMetadata(descriptor, &buffer);\n+\n+\t\t\t\t\t/*\n+\t\t\t\t\t * Allocate for post-processor to fill\n+\t\t\t\t\t * in JPEG related metadata.\n+\t\t\t\t\t */\n+\t\t\t\t\tif (!camera3Result->resultMetadata_)\n+\t\t\t\t\t\tcamera3Result->resultMetadata_ = getDynamicResultMetadata(metadata);\n+\t\t\t\t}\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t{\n \t\t/*\n-\t\t * Streams of type Direct have been queued to the\n-\t\t * libcamera::Camera and their acquire fences have\n-\t\t * already been waited on by the library.\n-\t\t *\n-\t\t * Acquire fences of streams of type Internal and Mapped\n-\t\t * will be handled during post-processing.\n+\t\t * Adding result to the request before sending it to the\n+\t\t * post-processing threads, so the streamProcessingComplete()\n+\t\t * slot can safely call compeleteResult().\n \t\t */\n-\t\tif (stream->type() == CameraStream::Type::Direct) {\n-\t\t\t/* If handling of the fence has failed restore buffer.fence. */\n-\t\t\tstd::unique_ptr<Fence> fence = buffer.frameBuffer->releaseFence();\n-\t\t\tif (fence)\n-\t\t\t\tbuffer.fence = fence->release();\n+\t\tMutexLocker lock(descriptor->resultsMutex_);\n+\t\tdescriptor->partialResults_.emplace_back(camera3Result);\n+\t}\n+\n+\t/*\n+\t * Queue all the post-processing streams request at once. The completion\n+\t * slot streamProcessingComplete() can only execute when we are out\n+\t * this critical section (result->streamsProcessMutex_). This helps to\n+\t * handle synchronous errors here itself.\n+\t */\n+\tfor (auto *buffer : camera3Result->pendingBuffersToProcess_) {\n+\t\tint ret = buffer->stream->process(buffer);\n+\t\tif (ret) {\n+\t\t\tsetBufferStatus(*buffer, StreamBuffer::Status::Error);\n+\t\t\tLOG(HAL, Error) << \"Failed to run post process of request \"\n+\t\t\t\t\t<< descriptor->frameNumber_;\n \t\t}\n-\t\tbuffer.status = StreamBuffer::Status::Success;\n \t}\n \n+\tif (!camera3Result->pendingBuffersToProcess_.empty())\n+\t\treturn;\n+\n+\tlocker.unlock();\n+\ttryCompleteResultDescriptor(camera3Result);\n+}\n+\n+void CameraDevice::requestComplete(Request *request)\n+{\n+\tCamera3RequestDescriptor *camera3Request =\n+\t\treinterpret_cast<Camera3RequestDescriptor *>(request->cookie());\n+\n \t/*\n-\t * If the Request has failed, abort the request by notifying the error\n-\t * and complete the request with all buffers in error state.\n+\t * On Android, each new partial result with a metadata must set a\n+\t * field (partial_result) to a distinct inclusive value between\n+\t * 1 and ANDROID_REQUEST_PARTIAL_RESULT_COUNT and the final result with\n+\t * metadata has to set the field as ANDROID_REQUEST_PARTIAL_RESULT_COUNT.\n+\t *\n+\t * An empty metadata with ANDROID_REQUEST_PARTIAL_RESULT_COUNT is not\n+\t * allowed. Add a result with a fixed metadata on requestComplete()\n+\t * in case all of previous metadata are sent early, and no more metadata\n+\t * can be sent as the final with ANDROID_REQUEST_PARTIAL_RESULT_COUNT.\n \t */\n-\tif (request->status() != Request::RequestComplete) {\n-\t\tLOG(HAL, Error) << \"Request \" << request->cookie()\n-\t\t\t\t<< \" not successfully completed: \"\n-\t\t\t\t<< request->status();\n+\tCamera3ResultDescriptor *result = new Camera3ResultDescriptor(camera3Request);\n+\tresult->resultMetadata_ = getFixedResultMetadata(camera3Request->settings_);\n+\tresult->metadataPackIndex_ = CameraCapabilities::MaxMetadataPackIndex;\n \n-\t\tabortRequest(descriptor);\n-\t\tcompleteDescriptor(descriptor);\n+\t{\n+\t\tMutexLocker lock(camera3Request->resultsMutex_);\n+\t\tcamera3Request->finalResult_.reset(result);\n \n-\t\treturn;\n+\t\tswitch (request->status()) {\n+\t\tcase Request::RequestComplete:\n+\t\t\tcamera3Request->status_ = Camera3RequestDescriptor::Status::Success;\n+\t\t\tbreak;\n+\t\tcase Request::RequestCancelled:\n+\t\t\tcamera3Request->status_ = Camera3RequestDescriptor::Status::Cancelled;\n+\t\t\tbreak;\n+\t\tcase Request::RequestPending:\n+\t\t\tLOG(HAL, Fatal) << \"Try to complete an unfinished request\";\n+\t\t\tbreak;\n+\t\t}\n \t}\n \n+\tcompleteResultDescriptor(result);\n+\treturn;\n+}\n+\n+void CameraDevice::tryCompleteResultDescriptor(Camera3ResultDescriptor *result)\n+{\n \t/*\n-\t * Notify shutter as soon as we have verified we have a valid request.\n+\t * Android requires buffers for a given stream must be returned in FIFO\n+\t * order. However, different streams are independent of each other, so\n+\t * it is acceptable and expected that the buffer for request 5 for\n+\t * stream A may be returned after the buffer for request 6 for stream\n+\t * B is. And it is acceptable that the result metadata for request 6\n+\t * for stream B is returned before the buffer for request 5 for stream\n+\t * A is. In result, if all of buffers of a result are the most front\n+\t * buffers of each stream, or the result contains no buffers, the result\n+\t * is allowed to send. Collect ready results in the order which follows\n+\t * the above rule.\n \t *\n-\t * \\todo The shutter event notification should be sent to the framework\n-\t * as soon as possible, earlier than request completion time.\n+\t * \\todo The reprocessing result can be returned ahead of the pending\n+\t * normal output results. But the FIFO ordering must be maintained for\n+\t * all reprocessing results. Track the reprocessing buffer's order\n+\t * independently when we have reprocessing API.\n \t */\n-\tuint64_t sensorTimestamp = static_cast<uint64_t>(request->metadata()\n-\t\t\t\t\t\t\t\t .get(controls::SensorTimestamp)\n-\t\t\t\t\t\t\t\t .value_or(0));\n-\tnotifyShutter(descriptor->frameNumber_, sensorTimestamp);\n+\tMutexLocker lock(pendingRequestMutex_);\n \n-\tLOG(HAL, Debug) << \"Request \" << request->cookie() << \" completed with \"\n-\t\t\t<< descriptor->request_->buffers().size() << \" streams\";\n+\tpendingResults_.push_front(result);\n+\tstd::list<Camera3ResultDescriptor *> readyResults;\n \n \t/*\n-\t * Generate the metadata associated with the captured buffers.\n-\t *\n-\t * Notify if the metadata generation has failed, but continue processing\n-\t * buffers and return an empty metadata pack.\n+\t * Error buffers do not have to follow the strict ordering valid buffers\n+\t * do, and ready to sent directly, removes them from the pendingBuffers\n+\t * so it won't blocking following valid buffers.\n \t */\n-\tdescriptor->resultMetadata_ = getResultMetadata(*descriptor);\n-\tif (!descriptor->resultMetadata_) {\n-\t\tnotifyError(descriptor->frameNumber_, nullptr, CAMERA3_MSG_ERROR_RESULT);\n+\tfor (auto &buffer : result->buffers_)\n+\t\tif (buffer->status == StreamBuffer::Status::Error)\n+\t\t\tpendingStreamBuffers_[buffer->stream].remove(buffer);\n \n-\t\t/*\n-\t\t * The camera framework expects an empty metadata pack on error.\n-\t\t *\n-\t\t * \\todo Check that the post-processor code handles this situation\n-\t\t * correctly.\n-\t\t */\n-\t\tdescriptor->resultMetadata_ = std::make_unique<CameraMetadata>(0, 0);\n+\t/*\n+\t * Exhaustly collect results which is ready to sent.\n+\t */\n+\tbool keepChecking;\n+\tdo {\n+\t\tkeepChecking = false;\n+\t\tauto iter = pendingResults_.begin();\n+\t\twhile (iter != pendingResults_.end()) {\n+\t\t\t/*\n+\t\t\t * A result is considered as ready when all of the valid\n+\t\t\t * buffers of the result are at the front of the pending\n+\t\t\t * buffers associated with its stream.\n+\t\t\t */\n+\t\t\tbool ready = true;\n+\t\t\tfor (auto &buffer : (*iter)->buffers_) {\n+\t\t\t\tif (buffer->status == StreamBuffer::Status::Error)\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tauto &pendingBuffers = pendingStreamBuffers_[buffer->stream];\n+\t\t\t\tASSERT(!pendingBuffers.empty());\n+\n+\t\t\t\tif (pendingBuffers.front() != buffer) {\n+\t\t\t\t\tready = false;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (!ready) {\n+\t\t\t\titer++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tfor (auto &buffer : (*iter)->buffers_)\n+\t\t\t\tif (buffer->status != StreamBuffer::Status::Error)\n+\t\t\t\t\tpendingStreamBuffers_[buffer->stream].pop_front();\n+\n+\t\t\t/* Keep checking since pendingStreamBuffers has updated */\n+\t\t\tkeepChecking = true;\n+\n+\t\t\treadyResults.push_back(*iter);\n+\t\t\titer = pendingResults_.erase(iter);\n+\t\t}\n+\t} while (keepChecking);\n+\n+\tlock.unlock();\n+\n+\tfor (auto &res : readyResults) {\n+\t\tcompleteResultDescriptor(res);\n \t}\n+}\n+\n+void CameraDevice::completeResultDescriptor(Camera3ResultDescriptor *result)\n+{\n+\tCamera3RequestDescriptor *request = result->request_;\n \n-\t/* Handle post-processing. */\n-\tMutexLocker locker(descriptor->streamsProcessMutex_);\n+\tMutexLocker lock(request->resultsMutex_);\n+\tresult->complete_ = true;\n \n \t/*\n-\t * Queue all the post-processing streams request at once. The completion\n-\t * slot streamProcessingComplete() can only execute when we are out\n-\t * this critical section. This helps to handle synchronous errors here\n-\t * itself.\n+\t * Android requires value of metadataPackIndex follows the rules:\n+\t *\n+\t * Set to MaxMetadataPackIndex if its the final result of the request.\n+\t * Set Incrementally from 1 if its the result containing metadata.\n+\t * Set to 0 if the result contains only buffers.\n+\t *\n+\t * The metadataPackIndex should be sent incrementally. Since the\n+\t * function may be called from post-processing threads, ensure the\n+\t * strict sending order by the mutex.\n \t */\n-\tauto iter = descriptor->pendingStreamsToProcess_.begin();\n-\twhile (iter != descriptor->pendingStreamsToProcess_.end()) {\n-\t\tCameraStream *stream = iter->first;\n-\t\tStreamBuffer *buffer = iter->second;\n+\tbool isFinalResult = (result == request->finalResult_.get());\n \n-\t\tif (stream->isJpegStream()) {\n-\t\t\tgenerateJpegExifMetadata(descriptor, buffer);\n-\t\t}\n+\tuint32_t &metadataPackIndex = result->metadataPackIndex_ = 0;\n \n-\t\tFrameBuffer *src = request->findBuffer(stream->stream());\n-\t\tif (!src) {\n-\t\t\tLOG(HAL, Error) << \"Failed to find a source stream buffer\";\n-\t\t\tsetBufferStatus(*buffer, StreamBuffer::Status::Error);\n-\t\t\titer = descriptor->pendingStreamsToProcess_.erase(iter);\n-\t\t\tcontinue;\n-\t\t}\n+\tif (isFinalResult)\n+\t\tmetadataPackIndex = CameraCapabilities::MaxMetadataPackIndex;\n+\telse if (result->resultMetadata_)\n+\t\tmetadataPackIndex = request->nextPartialResultIndex_++;\n+\telse\n+\t\tmetadataPackIndex = 0;\n \n-\t\tbuffer->srcBuffer = src;\n+\tif (metadataPackIndex > CameraCapabilities::MaxMetadataPackIndex)\n+\t\tLOG(HAL, Fatal) << \"Partial result count exceed limitation \"\n+\t\t\t\t<< CameraCapabilities::MaxMetadataPackIndex;\n \n-\t\t++iter;\n-\t\tint ret = stream->process(buffer);\n-\t\tif (ret) {\n-\t\t\tsetBufferStatus(*buffer, StreamBuffer::Status::Error);\n-\t\t\tdescriptor->pendingStreamsToProcess_.erase(stream);\n+\t/*\n+\t * The final result will be returned in completeRequestDescriptor()\n+\t * to follow the rules that the final result should be returned in\n+\t * its submission order.\n+\t */\n+\tif (!isFinalResult)\n+\t\tsendCaptureResult(result);\n \n-\t\t\t/*\n-\t\t\t * If the framebuffer is internal to CameraStream return\n-\t\t\t * it back now that we're done processing it.\n-\t\t\t */\n-\t\t\tif (buffer->internalBuffer)\n-\t\t\t\tstream->putBuffer(buffer->internalBuffer);\n+\tif (request->status_ == Camera3RequestDescriptor::Status::Pending)\n+\t\treturn;\n+\n+\t/*\n+\t * No more partial results shall be created once the status has\n+\t * changed from Pending. Check whether all of the pending\n+\t * results are sent before we complete the request. The function\n+\t * shall be re-called everytime a result is completed, and the final\n+\t * call shall complete the request.\n+\t */\n+\tbool hasPendingResult = false;\n+\tfor (auto &r : request->partialResults_) {\n+\t\tif (!r->complete_) {\n+\t\t\thasPendingResult = true;\n \t\t}\n \t}\n \n-\tif (descriptor->pendingStreamsToProcess_.empty()) {\n-\t\tlocker.unlock();\n-\t\tcompleteDescriptor(descriptor);\n-\t}\n-}\n+\tif (hasPendingResult)\n+\t\treturn;\n \n-/**\n- * \\brief Complete the Camera3RequestDescriptor\n- * \\param[in] descriptor The Camera3RequestDescriptor that has completed\n- *\n- * The function marks the Camera3RequestDescriptor as 'complete'. It shall be\n- * called when all the streams in the Camera3RequestDescriptor have completed\n- * capture (or have been generated via post-processing) and the request is ready\n- * to be sent back to the framework.\n- *\n- * \\context This function is \\threadsafe.\n- */\n-void CameraDevice::completeDescriptor(Camera3RequestDescriptor *descriptor)\n-{\n-\tMutexLocker lock(descriptorsMutex_);\n-\tdescriptor->complete_ = true;\n+\tlock.unlock();\n \n-\tsendCaptureResults();\n+\tcompleteRequestDescriptor(result->request_);\n }\n \n+\n /**\n- * \\brief Sequentially send capture results to the framework\n+ * \\brief Complete the Camera3RequestDescriptor\n+ * \\param[in] descriptor The Camera3RequestDescriptor\n  *\n- * Iterate over the descriptors queue to send completed descriptors back to the\n- * framework, in the same order as they have been queued. For each complete\n- * descriptor, populate a locally-scoped camera3_capture_result_t from the\n- * descriptor, send the capture result back by calling the\n- * process_capture_result() callback, and remove the descriptor from the queue.\n- * Stop iterating if the descriptor at the front of the queue is not complete.\n+ * The function shall be called when one of a partial result of the descriptor\n+ * has completed. The function shall complete the descriptor only when all of\n+ * the partial result has sent back to the framework, and send the final result\n+ * according to the submission order of the requests.\n  *\n- * This function should never be called directly in the codebase. Use\n- * completeDescriptor() instead.\n+ * \\context This function is \\threadsafe.\n  */\n-void CameraDevice::sendCaptureResults()\n+void CameraDevice::completeRequestDescriptor(Camera3RequestDescriptor *request)\n {\n-\twhile (!descriptors_.empty() && !descriptors_.front()->isPending()) {\n-\t\tauto descriptor = std::move(descriptors_.front());\n-\t\tdescriptors_.pop();\n-\n-\t\tcamera3_capture_result_t captureResult = {};\n-\n-\t\tcaptureResult.frame_number = descriptor->frameNumber_;\n-\n-\t\tif (descriptor->resultMetadata_)\n-\t\t\tcaptureResult.result =\n-\t\t\t\tdescriptor->resultMetadata_->getMetadata();\n-\n-\t\tstd::vector<camera3_stream_buffer_t> resultBuffers;\n-\t\tresultBuffers.reserve(descriptor->buffers_.size());\n+\tMutexLocker locker(pendingRequestMutex_);\n+\trequest->completed_ = true;\n \n-\t\tfor (auto &buffer : descriptor->buffers_) {\n-\t\t\tcamera3_buffer_status status = CAMERA3_BUFFER_STATUS_ERROR;\n-\n-\t\t\tif (buffer.status == StreamBuffer::Status::Success)\n-\t\t\t\tstatus = CAMERA3_BUFFER_STATUS_OK;\n+\t/*\n+\t * Android requires the final result of each request returns in\n+\t * the submission order.\n+\t */\n+\twhile (!pendingRequests_.empty()) {\n+\t\tauto &descriptor = pendingRequests_.front();\n+\t\tif (!descriptor->completed_)\n+\t\t\tbreak;\n \n-\t\t\t/*\n-\t\t\t * Pass the buffer fence back to the camera framework as\n-\t\t\t * a release fence. This instructs the framework to wait\n-\t\t\t * on the acquire fence in case we haven't done so\n-\t\t\t * ourselves for any reason.\n-\t\t\t */\n-\t\t\tresultBuffers.push_back({ buffer.stream->camera3Stream(),\n-\t\t\t\t\t\t  buffer.camera3Buffer, status,\n-\t\t\t\t\t\t  -1, buffer.fence.release() });\n+\t\tCamera3RequestDescriptor::Status status;\n+\t\t{\n+\t\t\tMutexLocker lock(descriptor->resultsMutex_);\n+\t\t\tstatus = descriptor->status_;\n+\t\t\tif (descriptor->finalResult_)\n+\t\t\t\tsendCaptureResult(descriptor->finalResult_.get());\n \t\t}\n \n-\t\tcaptureResult.num_output_buffers = resultBuffers.size();\n-\t\tcaptureResult.output_buffers = resultBuffers.data();\n-\n-\t\tif (descriptor->status_ == Camera3RequestDescriptor::Status::Success)\n-\t\t\tcaptureResult.partial_result = 1;\n+\t\t/*\n+\t\t * Call notify with CAMERA3_MSG_ERROR_RESULT to indicate some\n+\t\t * of the expected result metadata might not be available\n+\t\t * because the capture is cancelled by the camera. Only notify\n+\t\t * it when the final result is sent, since Android will ignore\n+\t\t * the following metadata.\n+\t\t */\n+\t\tif (status == Camera3RequestDescriptor::Status::Cancelled)\n+\t\t\tnotifyError(descriptor->frameNumber_, nullptr, CAMERA3_MSG_ERROR_RESULT);\n \n-\t\tcallbacks_->process_capture_result(callbacks_, &captureResult);\n+\t\tpendingRequests_.pop_front();\n \t}\n }\n \n void CameraDevice::setBufferStatus(StreamBuffer &streamBuffer,\n-\t\t\t\t   StreamBuffer::Status status)\n+\t\t\t\t   StreamBuffer::Status status) const\n {\n \tstreamBuffer.status = status;\n \tif (status != StreamBuffer::Status::Success) {\n \t\tnotifyError(streamBuffer.request->frameNumber_,\n \t\t\t    streamBuffer.stream->camera3Stream(),\n \t\t\t    CAMERA3_MSG_ERROR_BUFFER);\n+\t}\n+}\n+\n+void CameraDevice::sendPartialResult(Camera3ResultDescriptor *result)\n+{\n+\tCamera3RequestDescriptor *request = result->request_;\n+\n+\tMutexLocker lock(request->resultsMutex_);\n+\n+\t/*\n+\t * Android requires value of metadataPackIndex follows the rules:\n+\t *\n+\t * Set to 0: Result contains no metadata (buffers only).\n+\t * Set between 1 and (MaxMetadataPackIndex - 1): Result contains metadata.\n+\t * Set to MaxMetadataPackIndex: The result of the request.\n+\t */\n+\tuint32_t &metadataPackIndex = result->metadataPackIndex_ = 0;\n+\tif (result->resultMetadata_) {\n+\t\tmetadataPackIndex = request->nextPartialResultIndex_++;\n+\t}\n+\n+\tif (metadataPackIndex > CameraCapabilities::MaxMetadataPackIndex)\n+\t\tLOG(HAL, Fatal) << \"Partial result count exceed limitation \"\n+\t\t\t\t<< CameraCapabilities::MaxMetadataPackIndex;\n+\n+\t/*\n+\t * The metadataPackIndex should be sent incrementally. Since the\n+\t * function may be called from post-processing threads, ensure the\n+\t * strict sending order by the result mutex.\n+\t */\n+\tsendCaptureResult(result);\n+\tresult->complete_ = true;\n+}\n+\n+void CameraDevice::sendCaptureResult(Camera3ResultDescriptor *result) const\n+{\n+\tLOG(HAL, Debug) << \"Send result of frameNumber: \"\n+\t\t\t<< result->request_->frameNumber_\n+\t\t\t<< \" index: \" << result->metadataPackIndex_\n+\t\t\t<< \" has metadata: \" << (!!result->resultMetadata_)\n+\t\t\t<< \" has buffers \" << result->buffers_.size();\n+\n+\tcamera3_capture_result_t captureResult = {};\n \n-\t\t/* Also set error status on entire request descriptor. */\n-\t\tstreamBuffer.request->status_ =\n-\t\t\tCamera3RequestDescriptor::Status::Error;\n+\tcaptureResult.frame_number = result->request_->frameNumber_;\n+\n+\tstd::vector<camera3_stream_buffer_t> resultBuffers;\n+\tresultBuffers.reserve(result->buffers_.size());\n+\n+\tfor (auto &buffer : result->buffers_) {\n+\t\tcamera3_buffer_status status = CAMERA3_BUFFER_STATUS_ERROR;\n+\n+\t\tif (buffer->status == StreamBuffer::Status::Success)\n+\t\t\tstatus = CAMERA3_BUFFER_STATUS_OK;\n+\n+\t\t/*\n+\t\t * Pass the buffer fence back to the camera framework as\n+\t\t * a release fence. This instructs the framework to wait\n+\t\t * on the acquire fence in case we haven't done so\n+\t\t * ourselves for any reason.\n+\t\t */\n+\t\tresultBuffers.push_back({ buffer->stream->camera3Stream(),\n+\t\t\t\t\t  buffer->camera3Buffer, status,\n+\t\t\t\t\t  -1, buffer->fence.release() });\n \t}\n+\n+\tcaptureResult.num_output_buffers = resultBuffers.size();\n+\tcaptureResult.output_buffers = resultBuffers.data();\n+\n+\tif (result->resultMetadata_)\n+\t\tcaptureResult.result = result->resultMetadata_->getMetadata();\n+\n+\tcaptureResult.partial_result = result->metadataPackIndex_;\n+\n+\tcallbacks_->process_capture_result(callbacks_, &captureResult);\n }\n \n /**\n@@ -1354,35 +1620,28 @@ void CameraDevice::setBufferStatus(StreamBuffer &streamBuffer,\n  *\n  * This function is called from the post-processor's thread whenever a camera\n  * stream has finished post processing. The corresponding entry is dropped from\n- * the descriptor's pendingStreamsToProcess_ map.\n+ * the result's pendingBufferToProcess_ list.\n  *\n- * If the pendingStreamsToProcess_ map is then empty, all streams requiring to\n- * be generated from post-processing have been completed. Mark the descriptor as\n- * complete using completeDescriptor() in that case.\n+ * If the pendingBufferToProcess_ list is then empty, all streams requiring to\n+ * be generated from post-processing have been completed. Mark the result as\n+ * complete using completeResultDescriptor() in that case.\n  */\n void CameraDevice::streamProcessingComplete(StreamBuffer *streamBuffer,\n \t\t\t\t\t    StreamBuffer::Status status)\n {\n \tsetBufferStatus(*streamBuffer, status);\n+\tstreamBuffer->dstBuffer = nullptr;\n \n-\t/*\n-\t * If the framebuffer is internal to CameraStream return it back now\n-\t * that we're done processing it.\n-\t */\n-\tif (streamBuffer->internalBuffer)\n-\t\tstreamBuffer->stream->putBuffer(streamBuffer->internalBuffer);\n-\n-\tCamera3RequestDescriptor *request = streamBuffer->request;\n-\n+\tCamera3ResultDescriptor *result = streamBuffer->result;\n \t{\n-\t\tMutexLocker locker(request->streamsProcessMutex_);\n+\t\tMutexLocker locker(result->streamsProcessMutex_);\n+\t\tresult->pendingBuffersToProcess_.remove(streamBuffer);\n \n-\t\trequest->pendingStreamsToProcess_.erase(streamBuffer->stream);\n-\t\tif (!request->pendingStreamsToProcess_.empty())\n+\t\tif (!result->pendingBuffersToProcess_.empty())\n \t\t\treturn;\n \t}\n \n-\tcompleteDescriptor(streamBuffer->request);\n+\ttryCompleteResultDescriptor(streamBuffer->result);\n }\n \n std::string CameraDevice::logPrefix() const\n@@ -1414,6 +1673,99 @@ void CameraDevice::notifyError(uint32_t frameNumber, camera3_stream_t *stream,\n \tcallbacks_->notify(callbacks_, &notify);\n }\n \n+std::unique_ptr<CameraMetadata>\n+CameraDevice::getDynamicResultMetadata(const ControlList &metadata) const\n+{\n+\t/*\n+\t * \\todo Keep this in sync with the actual number of entries.\n+\t *\n+\t * Reserve capacity for the metadata larger than 4 bytes which cannot\n+\t * store in entries.\n+\t * Currently: 6 entries, 40 bytes extra capaticy.\n+\t *\n+\t * ANDROID_SENSOR_TIMESTAMP (int64) = 8 bytes\n+\t * ANDROID_SENSOR_EXPOSURE_TIME (int64) = 8 bytes\n+\t * ANDROID_SENSOR_FRAME_DURATION (int64) = 8 bytes\n+\t * ANDROID_SCALER_CROP_REGION (int32 X 4) = 16 bytes\n+\t * Total bytes for capacity: 40\n+\t *\n+\t * Reserve more capacity for the JPEG metadata set by the post-processor.\n+\t * Currently: 8 entries, 72 bytes extra capaticy.\n+\t *\n+\t * ANDROID_JPEG_GPS_COORDINATES (double x 3) = 24 bytes\n+\t * ANDROID_JPEG_GPS_PROCESSING_METHOD (byte x 32) = 32 bytes\n+\t * ANDROID_JPEG_GPS_TIMESTAMP (int64) = 8 bytes\n+\t * ANDROID_JPEG_SIZE (int32_t) = 4 bytes\n+\t * ANDROID_JPEG_QUALITY (byte) = 1 byte\n+\t * ANDROID_JPEG_ORIENTATION (int32_t) = 4 bytes\n+\t * ANDROID_JPEG_THUMBNAIL_QUALITY (byte) = 1 byte\n+\t * ANDROID_JPEG_THUMBNAIL_SIZE (int32 x 2) = 8 bytes\n+\t * Total bytes for JPEG metadata: 72\n+\t *\n+\t * \\todo Calculate the entries and capacity by the input ControlList.\n+\t */\n+\tstd::unique_ptr<CameraMetadata> resultMetadata =\n+\t\tstd::make_unique<CameraMetadata>(14, 112);\n+\tif (!resultMetadata->isValid()) {\n+\t\tLOG(HAL, Error) << \"Failed to allocate result metadata\";\n+\t\treturn nullptr;\n+\t}\n+\n+\t/* Add metadata tags reported by libcamera. */\n+\tconst auto &timestamp = metadata.get(controls::SensorTimestamp);\n+\tif (timestamp)\n+\t\tresultMetadata->addEntry(ANDROID_SENSOR_TIMESTAMP, *timestamp);\n+\n+\tconst auto &pipelineDepth = metadata.get(controls::draft::PipelineDepth);\n+\tif (pipelineDepth)\n+\t\tresultMetadata->addEntry(ANDROID_REQUEST_PIPELINE_DEPTH,\n+\t\t\t\t\t *pipelineDepth);\n+\n+\tconst auto &exposureTime = metadata.get(controls::ExposureTime);\n+\tif (exposureTime)\n+\t\tresultMetadata->addEntry(ANDROID_SENSOR_EXPOSURE_TIME,\n+\t\t\t\t\t *exposureTime * 1000ULL);\n+\n+\tconst auto &frameDuration = metadata.get(controls::FrameDuration);\n+\tif (frameDuration)\n+\t\tresultMetadata->addEntry(ANDROID_SENSOR_FRAME_DURATION,\n+\t\t\t\t\t *frameDuration * 1000);\n+\n+\tconst auto &scalerCrop = metadata.get(controls::ScalerCrop);\n+\tif (scalerCrop) {\n+\t\tconst Rectangle &crop = *scalerCrop;\n+\t\tint32_t cropRect[] = {\n+\t\t\tcrop.x,\n+\t\t\tcrop.y,\n+\t\t\tstatic_cast<int32_t>(crop.width),\n+\t\t\tstatic_cast<int32_t>(crop.height),\n+\t\t};\n+\t\tresultMetadata->addEntry(ANDROID_SCALER_CROP_REGION, cropRect);\n+\t}\n+\n+\tconst auto &testPatternMode = metadata.get(controls::draft::TestPatternMode);\n+\tif (testPatternMode)\n+\t\tresultMetadata->addEntry(ANDROID_SENSOR_TEST_PATTERN_MODE,\n+\t\t\t\t\t *testPatternMode);\n+\n+\t/*\n+\t * Return the result metadata pack even is not valid: get() will return\n+\t * nullptr.\n+\t */\n+\tif (!resultMetadata->isValid()) {\n+\t\tLOG(HAL, Error) << \"Failed to construct result metadata\";\n+\t}\n+\n+\tif (resultMetadata->resized()) {\n+\t\tauto [entryCount, dataCount] = resultMetadata->usage();\n+\t\tLOG(HAL, Info)\n+\t\t\t<< \"Result metadata resized: \" << entryCount\n+\t\t\t<< \" entries and \" << dataCount << \" bytes used\";\n+\t}\n+\n+\treturn resultMetadata;\n+}\n+\n /*\n  * Set jpeg metadata used to generate EXIF in the JPEG post processing.\n  */\n@@ -1424,10 +1776,8 @@ void CameraDevice::generateJpegExifMetadata(Camera3RequestDescriptor *request,\n \tauto &jpegExifMetadata = buffer->jpegExifMetadata;\n \tjpegExifMetadata.emplace(StreamBuffer::JpegExifMetadata());\n \n-\tjpegExifMetadata->sensorExposureTime = 0;\n-\tif (metadata.contains(controls::ExposureTime)) {\n-\t\tjpegExifMetadata->sensorExposureTime = metadata.get(controls::ExposureTime) * 1000ULL;\n-\t}\n+\tconst int64_t exposureTime = metadata.get(controls::ExposureTime).value_or(0);\n+\tjpegExifMetadata->sensorExposureTime = exposureTime;\n \n \t/*\n \t * todo: Android Sensitivity = analog gain X digital gain only on sensor.\n@@ -1441,31 +1791,26 @@ void CameraDevice::generateJpegExifMetadata(Camera3RequestDescriptor *request,\n  * Produce a set of fixed result metadata.\n  */\n std::unique_ptr<CameraMetadata>\n-CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) const\n+CameraDevice::getFixedResultMetadata(const CameraMetadata &settings) const\n {\n-\tconst ControlList &metadata = descriptor.request_->metadata();\n-\tconst CameraMetadata &settings = descriptor.settings_;\n \tcamera_metadata_ro_entry_t entry;\n \tbool found;\n \n \t/*\n+\t * \\todo Retrieve metadata from corresponding libcamera controls.\n \t * \\todo Keep this in sync with the actual number of entries.\n-\t * Currently: 40 entries, 156 bytes\n \t *\n-\t * Reserve more space for the JPEG metadata set by the post-processor.\n-\t * Currently:\n-\t * ANDROID_JPEG_GPS_COORDINATES (double x 3) = 24 bytes\n-\t * ANDROID_JPEG_GPS_PROCESSING_METHOD (byte x 32) = 32 bytes\n-\t * ANDROID_JPEG_GPS_TIMESTAMP (int64) = 8 bytes\n-\t * ANDROID_JPEG_SIZE (int32_t) = 4 bytes\n-\t * ANDROID_JPEG_QUALITY (byte) = 1 byte\n-\t * ANDROID_JPEG_ORIENTATION (int32_t) = 4 bytes\n-\t * ANDROID_JPEG_THUMBNAIL_QUALITY (byte) = 1 byte\n-\t * ANDROID_JPEG_THUMBNAIL_SIZE (int32 x 2) = 8 bytes\n-\t * Total bytes for JPEG metadata: 82\n+\t * Reserve capacity for the metadata larger than 4 bytes which cannot\n+\t * store in entries.\n+\t * Currently: 31 entries, 16 bytes\n+\t *\n+\t * ANDROID_CONTROL_AE_TARGET_FPS_RANGE (int32 X 2) = 8 bytes\n+\t * ANDROID_SENSOR_ROLLING_SHUTTER_SKEW (int64) = 8 bytes\n+\t *\n+\t * Total bytes: 16\n \t */\n \tstd::unique_ptr<CameraMetadata> resultMetadata =\n-\t\tstd::make_unique<CameraMetadata>(88, 166);\n+\t\tstd::make_unique<CameraMetadata>(31, 16);\n \tif (!resultMetadata->isValid()) {\n \t\tLOG(HAL, Error) << \"Failed to allocate result metadata\";\n \t\treturn nullptr;\n@@ -1563,9 +1908,6 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons\n \tresultMetadata->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,\n \t\t\t\t value);\n \n-\tvalue32 = ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;\n-\tresultMetadata->addEntry(ANDROID_SENSOR_TEST_PATTERN_MODE, value32);\n-\n \tvalue = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;\n \tresultMetadata->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, value);\n \n@@ -1587,40 +1929,6 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons\n \tresultMetadata->addEntry(ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,\n \t\t\t\t rolling_shutter_skew);\n \n-\t/* Add metadata tags reported by libcamera. */\n-\tconst int64_t timestamp = metadata.get(controls::SensorTimestamp).value_or(0);\n-\tresultMetadata->addEntry(ANDROID_SENSOR_TIMESTAMP, timestamp);\n-\n-\tconst auto &pipelineDepth = metadata.get(controls::draft::PipelineDepth);\n-\tif (pipelineDepth)\n-\t\tresultMetadata->addEntry(ANDROID_REQUEST_PIPELINE_DEPTH,\n-\t\t\t\t\t *pipelineDepth);\n-\n-\tconst auto &exposureTime = metadata.get(controls::ExposureTime);\n-\tif (exposureTime)\n-\t\tresultMetadata->addEntry(ANDROID_SENSOR_EXPOSURE_TIME,\n-\t\t\t\t\t *exposureTime * 1000ULL);\n-\n-\tconst auto &frameDuration = metadata.get(controls::FrameDuration);\n-\tif (frameDuration)\n-\t\tresultMetadata->addEntry(ANDROID_SENSOR_FRAME_DURATION,\n-\t\t\t\t\t *frameDuration * 1000);\n-\n-\tconst auto &scalerCrop = metadata.get(controls::ScalerCrop);\n-\tif (scalerCrop) {\n-\t\tconst Rectangle &crop = *scalerCrop;\n-\t\tint32_t cropRect[] = {\n-\t\t\tcrop.x, crop.y, static_cast<int32_t>(crop.width),\n-\t\t\tstatic_cast<int32_t>(crop.height),\n-\t\t};\n-\t\tresultMetadata->addEntry(ANDROID_SCALER_CROP_REGION, cropRect);\n-\t}\n-\n-\tconst auto &testPatternMode = metadata.get(controls::draft::TestPatternMode);\n-\tif (testPatternMode)\n-\t\tresultMetadata->addEntry(ANDROID_SENSOR_TEST_PATTERN_MODE,\n-\t\t\t\t\t *testPatternMode);\n-\n \t/*\n \t * Return the result metadata pack even is not valid: get() will return\n \t * nullptr.\ndiff --git a/src/android/camera_device.h b/src/android/camera_device.h\nindex 7b279895..613e419f 100644\n--- a/src/android/camera_device.h\n+++ b/src/android/camera_device.h\n@@ -20,6 +20,7 @@\n #include <libcamera/base/mutex.h>\n \n #include <libcamera/camera.h>\n+#include <libcamera/controls.h>\n #include <libcamera/framebuffer.h>\n #include <libcamera/geometry.h>\n #include <libcamera/pixel_format.h>\n@@ -29,7 +30,6 @@\n #include \"camera_capabilities.h\"\n #include \"camera_metadata.h\"\n #include \"camera_stream.h\"\n-#include \"jpeg/encoder.h\"\n \n class Camera3RequestDescriptor;\n struct CameraConfigData;\n@@ -63,6 +63,8 @@ public:\n \tconst camera_metadata_t *constructDefaultRequestSettings(int type);\n \tint configureStreams(camera3_stream_configuration_t *stream_list);\n \tint processCaptureRequest(camera3_capture_request_t *request);\n+\tvoid partialResultComplete(libcamera::Request *request,\n+\t\t\t\t   libcamera::Result *result);\n \tvoid requestComplete(libcamera::Request *request);\n \tvoid streamProcessingComplete(StreamBuffer *bufferStream,\n \t\t\t\t      StreamBuffer::Status status);\n@@ -87,20 +89,25 @@ private:\n \tcreateFrameBuffer(const buffer_handle_t camera3buffer,\n \t\t\t  libcamera::PixelFormat pixelFormat,\n \t\t\t  const libcamera::Size &size);\n-\tvoid abortRequest(Camera3RequestDescriptor *descriptor) const;\n \tbool isValidRequest(camera3_capture_request_t *request) const;\n \tvoid notifyShutter(uint32_t frameNumber, uint64_t timestamp);\n \tvoid notifyError(uint32_t frameNumber, camera3_stream_t *stream,\n \t\t\t camera3_error_msg_code code) const;\n \tint processControls(Camera3RequestDescriptor *descriptor);\n-\tvoid completeDescriptor(Camera3RequestDescriptor *descriptor)\n-\t\tLIBCAMERA_TSA_EXCLUDES(descriptorsMutex_);\n-\tvoid sendCaptureResults() LIBCAMERA_TSA_REQUIRES(descriptorsMutex_);\n-\tvoid setBufferStatus(StreamBuffer &buffer, StreamBuffer::Status status);\n+\tvoid abortRequest(Camera3RequestDescriptor *descriptor);\n+\tvoid tryCompleteResultDescriptor(Camera3ResultDescriptor *result);\n+\tvoid completeResultDescriptor(Camera3ResultDescriptor *result);\n+\tvoid completeRequestDescriptor(Camera3RequestDescriptor *descriptor);\n+\tvoid sendPartialResult(Camera3ResultDescriptor *result);\n+\tvoid sendCaptureResult(Camera3ResultDescriptor *partialResult) const;\n+\tvoid setBufferStatus(StreamBuffer &buffer, StreamBuffer::Status status) const;\n \tvoid generateJpegExifMetadata(Camera3RequestDescriptor *request,\n \t\t\t\t      StreamBuffer *buffer) const;\n-\tstd::unique_ptr<CameraMetadata> getResultMetadata(\n-\t\tconst Camera3RequestDescriptor &descriptor) const;\n+\n+\tstd::unique_ptr<CameraMetadata> getDynamicResultMetadata(\n+\t\tconst libcamera::ControlList &metadata) const;\n+\tstd::unique_ptr<CameraMetadata> getFixedResultMetadata(\n+\t\tconst CameraMetadata &settings) const;\n \n \tunsigned int id_;\n \tcamera3_device_t camera3Device_;\n@@ -117,9 +124,16 @@ private:\n \n \tstd::vector<CameraStream> streams_;\n \n-\tlibcamera::Mutex descriptorsMutex_ LIBCAMERA_TSA_ACQUIRED_AFTER(stateMutex_);\n-\tstd::queue<std::unique_ptr<Camera3RequestDescriptor>> descriptors_\n-\t\tLIBCAMERA_TSA_GUARDED_BY(descriptorsMutex_);\n+\tlibcamera::Mutex pendingRequestMutex_ LIBCAMERA_TSA_ACQUIRED_AFTER(stateMutex_);\n+\n+\tstd::list<std::unique_ptr<Camera3RequestDescriptor>> pendingRequests_\n+\t\tLIBCAMERA_TSA_GUARDED_BY(pendingRequestMutex_);\n+\n+\tstd::list<Camera3ResultDescriptor *> pendingResults_\n+\t\tLIBCAMERA_TSA_GUARDED_BY(pendingRequestMutex_);\n+\n+\tstd::map<CameraStream *, std::list<StreamBuffer *>> pendingStreamBuffers_\n+\t\tLIBCAMERA_TSA_GUARDED_BY(pendingRequestMutex_);\n \n \tstd::string maker_;\n \tstd::string model_;\ndiff --git a/src/android/camera_request.cpp b/src/android/camera_request.cpp\nindex f5d4d314..ab829760 100644\n--- a/src/android/camera_request.cpp\n+++ b/src/android/camera_request.cpp\n@@ -7,9 +7,11 @@\n \n #include \"camera_request.h\"\n \n+#include <libcamera/base/log.h>\n #include <libcamera/base/span.h>\n \n #include \"camera_buffer.h\"\n+#include \"camera_stream.h\"\n \n using namespace libcamera;\n \n@@ -42,25 +44,25 @@ using namespace libcamera;\n  * │  processCaptureRequest(camera3_capture_request_t request)   │\n  * │                                                             │\n  * │   - Create Camera3RequestDescriptor tracking this request   │\n- * │   - Streams requiring post-processing are stored in the     │\n- * │     pendingStreamsToProcess map                             │\n+ * │   - Buffer requiring post-processing are marked by the      │\n+ * │     CameraStream::Type as Mapped or Internal                │\n  * │   - Add this Camera3RequestDescriptor to descriptors' queue │\n  * │     CameraDevice::descriptors_                              │\n- * │                                                             │ ┌─────────────────────────┐\n- * │   - Queue the capture request to libcamera core ────────────┤►│libcamera core           │\n- * │                                                             │ ├─────────────────────────┤\n- * │                                                             │ │- Capture from Camera    │\n- * │                                                             │ │                         │\n- * │                                                             │ │- Emit                   │\n- * │                                                             │ │  Camera::requestComplete│\n- * │  requestCompleted(Request *request) ◄───────────────────────┼─┼────                     │\n- * │                                                             │ │                         │\n- * │   - Check request completion status                         │ └─────────────────────────┘\n+ * │                                                             │ ┌───────────────────────────────┐\n+ * │   - Queue the capture request to libcamera core ────────────┤►│libcamera core                 │\n+ * │                                                             │ ├───────────────────────────────┤\n+ * │                                                             │ │- Capture from Camera          │\n+ * │                                                             │ │                               │\n+ * │                                                             │ │- Emit                         │\n+ * │                                                             │ │  Camera::partialResultComplete│\n+ * │  partialResultComplete(Request *request, Result result*) ◄──┼─┼────                           │\n+ * │                                                             │ │                               │\n+ * │   - Check request completion status                         │ └───────────────────────────────┘\n  * │                                                             │\n- * │   - if (pendingStreamsToProcess > 0)                        │\n- * │      Queue all entries from pendingStreamsToProcess         │\n+ * │   - if (pendingBuffersToProcess > 0)                        │\n+ * │      Queue all entries from pendingBuffersToProcess         │\n  * │    else                                   │                 │\n- * │      completeDescriptor()                 │                 └──────────────────────┐\n+ * │      completeResultDescriptor()           │                 └──────────────────────┐\n  * │                                           │                                        │\n  * │                ┌──────────────────────────┴───┬──────────────────┐                 │\n  * │                │                              │                  │                 │\n@@ -93,10 +95,10 @@ using namespace libcamera;\n  * │ |                                       |     |              |                     │\n  * │ | - Check and set buffer status         |     |     ....     |                     │\n  * │ | - Remove post+processing entry        |     |              |                     │\n- * │ |   from pendingStreamsToProcess        |     |              |                     │\n+ * │ |   from pendingBuffersToProcess        |     |              |                     │\n  * │ |                                       |     |              |                     │\n- * │ | - if (pendingStreamsToProcess.empty())|     |              |                     │\n- * │ |        completeDescriptor             |     |              |                     │\n+ * │ | - if (pendingBuffersToProcess.empty())|     |              |                     │\n+ * │ |        completeResultDescriptor       |     |              |                     │\n  * │ |                                       |     |              |                     │\n  * │ +---------------------------------------+     +--------------+                     │\n  * │                                                                                    │\n@@ -110,6 +112,7 @@ using namespace libcamera;\n \n Camera3RequestDescriptor::Camera3RequestDescriptor(\n \tCamera *camera, const camera3_capture_request_t *camera3Request)\n+\t: status_(Status::Pending), nextPartialResultIndex_(1), completed_(false)\n {\n \tframeNumber_ = camera3Request->frame_number;\n \n@@ -138,7 +141,27 @@ Camera3RequestDescriptor::Camera3RequestDescriptor(\n \trequest_ = camera->createRequest(reinterpret_cast<uint64_t>(this));\n }\n \n-Camera3RequestDescriptor::~Camera3RequestDescriptor() = default;\n+Camera3RequestDescriptor::~Camera3RequestDescriptor()\n+{\n+\t/*\n+\t * Recycle the allocated internal buffer back to its source stream.\n+\t */\n+\tfor (auto &[sourceStream, frameBuffer] : internalBuffers_)\n+\t\tsourceStream->putBuffer(frameBuffer);\n+}\n+\n+/*\n+ * \\class Camera3ResultDescriptor\n+ *\n+ * A utility class that groups information about a capture result to be later\n+ * sent to framework.\n+ */\n+Camera3ResultDescriptor::Camera3ResultDescriptor(Camera3RequestDescriptor *request)\n+\t: request_(request), metadataPackIndex_(1), complete_(false)\n+{\n+}\n+\n+Camera3ResultDescriptor::~Camera3ResultDescriptor() = default;\n \n /**\n  * \\class StreamBuffer\ndiff --git a/src/android/camera_request.h b/src/android/camera_request.h\nindex f91de955..fd8dad24 100644\n--- a/src/android/camera_request.h\n+++ b/src/android/camera_request.h\n@@ -24,6 +24,7 @@\n \n class CameraBuffer;\n class CameraStream;\n+class Camera3ResultDescriptor;\n class Camera3RequestDescriptor;\n \n class StreamBuffer\n@@ -52,45 +53,74 @@ public:\n \tstd::unique_ptr<libcamera::FrameBuffer> frameBuffer;\n \tlibcamera::UniqueFD fence;\n \tStatus status = Status::Success;\n-\tlibcamera::FrameBuffer *internalBuffer = nullptr;\n \tconst libcamera::FrameBuffer *srcBuffer = nullptr;\n \tstd::unique_ptr<CameraBuffer> dstBuffer;\n \tstd::optional<JpegExifMetadata> jpegExifMetadata;\n+\tCamera3ResultDescriptor *result;\n \tCamera3RequestDescriptor *request;\n \n private:\n \tLIBCAMERA_DISABLE_COPY(StreamBuffer)\n };\n \n+class Camera3ResultDescriptor\n+{\n+public:\n+\tCamera3ResultDescriptor(Camera3RequestDescriptor *request);\n+\t~Camera3ResultDescriptor();\n+\n+\tCamera3RequestDescriptor *request_;\n+\tuint32_t metadataPackIndex_;\n+\n+\tstd::unique_ptr<CameraMetadata> resultMetadata_;\n+\tstd::vector<StreamBuffer *> buffers_;\n+\n+\t/* Keeps track of buffers waiting for post-processing. */\n+\tstd::list<StreamBuffer *> pendingBuffersToProcess_\n+\t\tLIBCAMERA_TSA_GUARDED_BY(streamsProcessMutex_);\n+\tlibcamera::Mutex streamsProcessMutex_;\n+\n+\tbool complete_;\n+\n+private:\n+\tLIBCAMERA_DISABLE_COPY(Camera3ResultDescriptor)\n+};\n+\n class Camera3RequestDescriptor\n {\n public:\n \tenum class Status {\n+\t\tPending,\n \t\tSuccess,\n-\t\tError,\n+\t\tCancelled,\n+\t\tAborted,\n \t};\n \n-\t/* Keeps track of streams requiring post-processing. */\n-\tstd::map<CameraStream *, StreamBuffer *> pendingStreamsToProcess_\n-\t\tLIBCAMERA_TSA_GUARDED_BY(streamsProcessMutex_);\n-\tlibcamera::Mutex streamsProcessMutex_;\n-\n \tCamera3RequestDescriptor(libcamera::Camera *camera,\n \t\t\t\t const camera3_capture_request_t *camera3Request);\n \t~Camera3RequestDescriptor();\n \n-\tbool isPending() const { return !complete_; }\n-\n \tuint32_t frameNumber_ = 0;\n \n \tstd::vector<StreamBuffer> buffers_;\n-\n \tCameraMetadata settings_;\n+\n \tstd::unique_ptr<libcamera::Request> request_;\n-\tstd::unique_ptr<CameraMetadata> resultMetadata_;\n \n-\tbool complete_ = false;\n-\tStatus status_ = Status::Success;\n+\tstd::map<CameraStream *, libcamera::FrameBuffer *> internalBuffers_;\n+\n+\tStatus status_\n+\t\tLIBCAMERA_TSA_GUARDED_BY(resultsMutex_);\n+\tuint32_t nextPartialResultIndex_\n+\t\tLIBCAMERA_TSA_GUARDED_BY(resultsMutex_);\n+\tstd::unique_ptr<Camera3ResultDescriptor> finalResult_\n+\t\tLIBCAMERA_TSA_GUARDED_BY(resultsMutex_);\n+\tstd::vector<std::unique_ptr<Camera3ResultDescriptor>> partialResults_\n+\t\tLIBCAMERA_TSA_GUARDED_BY(resultsMutex_);\n+\n+\tlibcamera::Mutex resultsMutex_;\n+\n+\tbool completed_;\n \n private:\n \tLIBCAMERA_DISABLE_COPY(Camera3RequestDescriptor)\ndiff --git a/src/android/camera_stream.cpp b/src/android/camera_stream.cpp\nindex 02dc8922..3215025c 100644\n--- a/src/android/camera_stream.cpp\n+++ b/src/android/camera_stream.cpp\n@@ -187,6 +187,7 @@ int CameraStream::process(StreamBuffer *streamBuffer)\n \t\t*streamBuffer->camera3Buffer, output.pixelFormat, output.size,\n \t\tPROT_READ | PROT_WRITE);\n \tif (!streamBuffer->dstBuffer->isValid()) {\n+\t\tstreamBuffer->dstBuffer = nullptr;\n \t\tLOG(HAL, Error) << \"Failed to create destination buffer\";\n \t\treturn -EINVAL;\n \t}\ndiff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp\nindex 7e3713f5..d61c566b 100644\n--- a/src/android/jpeg/post_processor_jpeg.cpp\n+++ b/src/android/jpeg/post_processor_jpeg.cpp\n@@ -111,7 +111,7 @@ void PostProcessorJpeg::process(StreamBuffer *streamBuffer)\n \tASSERT(jpegExifMetadata.has_value());\n \n \tconst CameraMetadata &requestMetadata = streamBuffer->request->settings_;\n-\tCameraMetadata *resultMetadata = streamBuffer->request->resultMetadata_.get();\n+\tCameraMetadata *resultMetadata = streamBuffer->result->resultMetadata_.get();\n \tcamera_metadata_ro_entry_t entry;\n \tint ret;\n \n",
    "prefixes": [
        "libcamera-devel",
        "v2",
        "6/7"
    ]
}