{"id":15256,"url":"https://patchwork.libcamera.org/api/1.1/patches/15256/?format=json","web_url":"https://patchwork.libcamera.org/patch/15256/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20220106094323.1819801-2-hiroh@chromium.org>","date":"2022-01-06T09:43:23","name":"[libcamera-devel,1/1] android: camera_device: Configure one stream for identical stream requests","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"60584e53efd91ae0315c8cfab79624a450c3608b","submitter":{"id":63,"url":"https://patchwork.libcamera.org/api/1.1/people/63/?format=json","name":"Hirokazu Honda","email":"hiroh@chromium.org"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/15256/mbox/","series":[{"id":2880,"url":"https://patchwork.libcamera.org/api/1.1/series/2880/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=2880","date":"2022-01-06T09:43:22","name":"Configure one stream for identical stream requests","version":1,"mbox":"https://patchwork.libcamera.org/series/2880/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/15256/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/15256/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\r\n\t[92.243.16.209])\r\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id F224ABF415\r\n\tfor <parsemail@patchwork.libcamera.org>;\r\n\tThu,  6 Jan 2022 09:43:35 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\r\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A1C6660928;\r\n\tThu,  6 Jan 2022 10:43:35 +0100 (CET)","from mail-pj1-x1036.google.com (mail-pj1-x1036.google.com\r\n\t[IPv6:2607:f8b0:4864:20::1036])\r\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id BCAF5604F5\r\n\tfor <libcamera-devel@lists.libcamera.org>;\r\n\tThu,  6 Jan 2022 10:43:33 +0100 (CET)","by mail-pj1-x1036.google.com with SMTP id\r\n\trj2-20020a17090b3e8200b001b1944bad25so2573669pjb.5\r\n\tfor <libcamera-devel@lists.libcamera.org>;\r\n\tThu, 06 Jan 2022 01:43:33 -0800 (PST)","from hiroh2.tok.corp.google.com\r\n\t([2401:fa00:8f:203:cb79:fed4:e097:2139])\r\n\tby smtp.gmail.com with ESMTPSA id\r\n\t4sm1844860pfy.191.2022.01.06.01.43.30\r\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\r\n\tThu, 06 Jan 2022 01:43:31 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com;\r\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\r\n\tunprotected) header.d=chromium.org header.i=@chromium.org\r\n\theader.b=\"C/kOC22X\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\r\n\ts=google; \r\n\th=from:to:cc:subject:date:message-id:in-reply-to:references\r\n\t:mime-version:content-transfer-encoding;\r\n\tbh=KyuDPm/zxDMeMdQNU7I6ndUbNpxweiggVhp2DAYM1/4=;\r\n\tb=C/kOC22XlKn9ctJizGH8yZh+Ra7Qb6BVDu+q1/SnMB6sZXyUTdGCsjY8RMh3Lo+BLr\r\n\tzNhCB976mKnkqogqkILy+y4ELyFdMi1I3vOoVt56E5mvLDa+Vu/j5SVQ/UJ0Xm37YbsJ\r\n\toslmVDjbAu0OCCUy8m8QS1zC9i9yOIG/g/SMw=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\r\n\td=1e100.net; s=20210112;\r\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\r\n\t:references:mime-version:content-transfer-encoding;\r\n\tbh=KyuDPm/zxDMeMdQNU7I6ndUbNpxweiggVhp2DAYM1/4=;\r\n\tb=8A0XisMcPJQrJpB5A59lgv7Ga0uBxN/uWMXVYoRybRZps2P5imaSnmoMxAw2WkVf5N\r\n\t2d2LwDlJ8KVatBUoq3rdcT7PRDLxzlVqOKV9c4ckIqtA6uqUlfLHV0JNCf2GGUCn/+n0\r\n\t6z0HbhDHlbx7FjZsYe8dzM3LQBitA+Z1Qt22NJeIMEU8eamGSAfyGHB7FP+B2rKBIEEL\r\n\tRPpnDzasCYwE6ENXj5YJrURNGaySfT0L129Z3bjd0GBQ17L5Ykd0mGr+KLKa0YV/mRmZ\r\n\tPfJ0y7junBVjm4gulK8EhQForB1mGa1j68qZX27pHM9VlkFUIkIr7TezwRK1SmInOx+7\r\n\txP8A==","X-Gm-Message-State":"AOAM531qwf+vyf5AAsLYpGJBWI3+mRe8OfoXEOcZt1SQad22A+gG+Bls\r\n\t/yYBD6MqdBoWiYkfn/4cPPjqE6fXPIFAzw==","X-Google-Smtp-Source":"ABdhPJwgeOX6H3BbI/LPNBfTljMqNkP3l7TJg8HzkYPlRak1iViDQwOQnbyRAAILYe20AV60U0HFYw==","X-Received":"by 2002:a17:90b:3b49:: with SMTP id\r\n\tot9mr9155941pjb.110.1641462211678; \r\n\tThu, 06 Jan 2022 01:43:31 -0800 (PST)","From":"Hirokazu Honda <hiroh@chromium.org>","To":"libcamera-devel@lists.libcamera.org","Date":"Thu,  6 Jan 2022 18:43:23 +0900","Message-Id":"<20220106094323.1819801-2-hiroh@chromium.org>","X-Mailer":"git-send-email 2.34.1.448.ga2b2bfdf31-goog","In-Reply-To":"<20220106094323.1819801-1-hiroh@chromium.org>","References":"<20220106094323.1819801-1-hiroh@chromium.org>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH 1/1] android: camera_device: Configure one\r\n\tstream for identical stream requests","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>,\r\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>,\r\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"An Android HAL client may request multiple identical streams. It is\nredundant that a native camera device produces a separate stream for\neach of the identical requests. Configure the camera with a single\nstream in that case. The other identical HAL streams will be produced by\nthe YUV post-processor.\n\nThe Android HAL client can provide capture requests that are resolved as\nthey are produced by YUV post-processor. Then a buffer to be filled a\ncamera is not given. So the HAL adaptation layer looks up buffer\ninformation to be passed to a camera and allocate the buffer by using\nPlatformBufferAllocator.\n\nSigned-off-by: Hirokazu Honda <hiroh@chromium.org>\n---\n src/android/camera_device.cpp | 84 ++++++++++++++++++++++++++++++++++-\n src/android/camera_request.h  |  2 +\n src/android/camera_stream.cpp | 12 ++++-\n src/android/camera_stream.h   |  6 ++-\n 4 files changed, 100 insertions(+), 4 deletions(-)\n\n--\n2.34.1.448.ga2b2bfdf31-goog","diff":"diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\r\nindex 83825736..53533064 100644\r\n--- a/src/android/camera_device.cpp\r\n+++ b/src/android/camera_device.cpp\r\n@@ -9,6 +9,7 @@\r\n\r\n #include <algorithm>\r\n #include <fstream>\r\n+#include <set>\r\n #include <sys/mman.h>\r\n #include <unistd.h>\r\n #include <vector>\r\n@@ -604,6 +605,35 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)\r\n \t\t\tcontinue;\r\n \t\t}\r\n\r\n+\t\t/*\r\n+\t\t * While gralloc usage flags are supposed to report usage\r\n+\t\t * patterns to select a suitable buffer allocation strategy, in\r\n+\t\t * practice they're also used to make other decisions, such as\r\n+\t\t * selecting the actual format for the IMPLEMENTATION_DEFINED\r\n+\t\t * HAL pixel format. To avoid issues, we thus have to set the\r\n+\t\t * GRALLOC_USAGE_HW_CAMERA_WRITE flag unconditionally, even for\r\n+\t\t * streams that will be produced in software.\r\n+\t\t */\r\n+\t\tstream->usage |= GRALLOC_USAGE_HW_CAMERA_WRITE;\r\n+\r\n+\t\t/*\r\n+\t\t * If a CameraStream with the same size and format of the\r\n+\t\t * current stream has already been requested, associate the two.\r\n+\t\t */\r\n+\t\tauto iter = std::find_if(\r\n+\t\t\tstreamConfigs.begin(), streamConfigs.end(),\r\n+\t\t\t[size, format](const Camera3StreamConfig &streamConfig) {\r\n+\t\t\t\treturn streamConfig.config.size == size &&\r\n+\t\t\t\t       streamConfig.config.pixelFormat == format;\r\n+\t\t\t});\r\n+\t\tif (iter != streamConfigs.end()) {\r\n+\t\t\t/* Add usage to copy the buffer in streams[0] to stream. */\r\n+\t\t\titer->streams[0].stream->usage |= GRALLOC_USAGE_SW_READ_OFTEN;\r\n+\t\t\tstream->usage |= GRALLOC_USAGE_SW_WRITE_OFTEN;\r\n+\t\t\titer->streams.push_back({ stream, CameraStream::Type::Mapped });\r\n+\t\t\tcontinue;\r\n+\t\t}\r\n+\r\n \t\tCamera3StreamConfig streamConfig;\r\n \t\tstreamConfig.streams = { { stream, CameraStream::Type::Direct } };\r\n \t\tstreamConfig.config.size = size;\r\n@@ -681,10 +711,32 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)\r\n \tfor (const auto &streamConfig : streamConfigs) {\r\n \t\tconfig->addConfiguration(streamConfig.config);\r\n\r\n+\t\tCameraStream *directStream = nullptr;\r\n \t\tfor (auto &stream : streamConfig.streams) {\r\n+\t\t\tCameraStream *sourceStream = nullptr;\r\n+\r\n+\t\t\t/*\r\n+\t\t\t * Sets the source stream for Mapped type streams to the\r\n+\t\t\t * same Direct type stream. The Direct type stream is\r\n+\t\t\t * put earlier than Mapped type streams in the current\r\n+\t\t\t * implementation. This might be broken in the future.\r\n+\t\t\t *\r\n+\t\t\t * \\todo Remove this order assumption.\r\n+\t\t\t */\r\n+\t\t\tif (stream.type == CameraStream::Type::Mapped) {\r\n+\t\t\t\tASSERT(directStream);\r\n+\t\t\t\tsourceStream = directStream;\r\n+\t\t\t}\r\n+\r\n \t\t\tstreams_.emplace_back(this, config.get(), stream.type,\r\n-\t\t\t\t\t      stream.stream, config->size() - 1);\r\n+\t\t\t\t\t      stream.stream,\r\n+\t\t\t\t\t      sourceStream,\r\n+\t\t\t\t\t      config->size() - 1);\r\n \t\t\tstream.stream->priv = static_cast<void *>(&streams_.back());\r\n+\t\t\tif (!directStream &&\r\n+\t\t\t    stream.type == CameraStream::Type::Direct) {\r\n+\t\t\t\tdirectStream = &streams_.back();\r\n+\t\t\t}\r\n \t\t}\r\n \t}\r\n\r\n@@ -917,6 +969,8 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\r\n \tLOG(HAL, Debug) << \"Queueing request \" << descriptor->request_->cookie()\r\n \t\t\t<< \" with \" << descriptor->buffers_.size() << \" streams\";\r\n\r\n+\tstd::set<CameraStream *> requiredStreams;\r\n+\tstd::set<CameraStream *> providedStreams;\r\n \tfor (const auto &[i, buffer] : utils::enumerate(descriptor->buffers_)) {\r\n \t\tCameraStream *cameraStream = buffer.stream;\r\n \t\tcamera3_stream_t *camera3Stream = cameraStream->camera3Stream();\r\n@@ -947,6 +1001,8 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\r\n\r\n \t\t\tdescriptor->pendingStreamsToProcess_.insert(\r\n \t\t\t\t{ cameraStream, &buffer });\r\n+\t\t\tASSERT(cameraStream->sourceStream());\r\n+\t\t\trequiredStreams.insert(cameraStream->sourceStream());\r\n \t\t\tcontinue;\r\n\r\n \t\tcase CameraStream::Type::Direct:\r\n@@ -990,8 +1046,32 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\r\n \t\tauto fence = std::make_unique<Fence>(std::move(acquireFence));\r\n \t\tdescriptor->request_->addBuffer(cameraStream->stream(),\r\n \t\t\t\t\t\tframeBuffer, std::move(fence));\r\n+\t\tprovidedStreams.insert(cameraStream);\r\n \t}\r\n\r\n+\tfor (CameraStream *requiredStream : requiredStreams) {\r\n+\t\tif (providedStreams.find(requiredStream) != providedStreams.end())\r\n+\t\t\tcontinue;\r\n+\r\n+\t\t/* \\todo Can we Map stream to Internal type stream? */\r\n+\t\tASSERT(requiredStream->type() == CameraStream::Type::Direct);\r\n+\r\n+\t\tFrameBuffer *frameBuffer = requiredStream->getBuffer();\r\n+\t\tif (!frameBuffer) {\r\n+\t\t\tLOG(HAL, Error) << \"Failed to create frame buffer\";\r\n+\t\t\treturn -ENOMEM;\r\n+\t\t}\r\n+\r\n+\t\t/*\r\n+\t\t * addBuffer() lets a buffer for requiredStream be output by\r\n+\t\t * camera. Records frameBuffer with requiredStream to call\r\n+\t\t * putBuffer() after post-processing is complete.\r\n+\t\t */\r\n+\t\tdescriptor->request_->addBuffer(requiredStream->stream(),\r\n+\t\t\t\t\t\tframeBuffer, nullptr);\r\n+\t\tMutexLocker lock(descriptor->streamsProcessMutex_);\r\n+\t\tdescriptor->putBuffers_.push_back({ requiredStream, frameBuffer });\r\n+\t}\r\n \t/*\r\n \t * Translate controls from Android to libcamera and queue the request\r\n \t * to the camera.\r\n@@ -1251,6 +1331,8 @@ void CameraDevice::streamProcessingComplete(Camera3RequestDescriptor::StreamBuff\r\n \t\trequest->pendingStreamsToProcess_.erase(streamBuffer->stream);\r\n \t\tif (!request->pendingStreamsToProcess_.empty())\r\n \t\t\treturn;\r\n+\t\tfor (auto [cameraStream, buffer] : request->putBuffers_)\r\n+\t\t\tcameraStream->putBuffer(buffer);\r\n \t}\r\n\r\n \tcompleteDescriptor(streamBuffer->request);\r\ndiff --git a/src/android/camera_request.h b/src/android/camera_request.h\r\nindex 37b6ae32..7a41c6d9 100644\r\n--- a/src/android/camera_request.h\r\n+++ b/src/android/camera_request.h\r\n@@ -59,6 +59,8 @@ public:\r\n \t/* Keeps track of streams requiring post-processing. */\r\n \tstd::map<CameraStream *, StreamBuffer *> pendingStreamsToProcess_\r\n \t\tLIBCAMERA_TSA_GUARDED_BY(streamsProcessMutex_);\r\n+\tstd::vector<std::pair<CameraStream *, libcamera::FrameBuffer *>> putBuffers_\r\n+\t\tLIBCAMERA_TSA_GUARDED_BY(streamsProcessMutex_);\r\n \tlibcamera::Mutex streamsProcessMutex_;\r\n\r\n \tCamera3RequestDescriptor(libcamera::Camera *camera,\r\ndiff --git a/src/android/camera_stream.cpp b/src/android/camera_stream.cpp\r\nindex c2157450..634cf319 100644\r\n--- a/src/android/camera_stream.cpp\r\n+++ b/src/android/camera_stream.cpp\r\n@@ -52,9 +52,11 @@ LOG_DECLARE_CATEGORY(HAL)\r\n\r\n CameraStream::CameraStream(CameraDevice *const cameraDevice,\r\n \t\t\t   CameraConfiguration *config, Type type,\r\n-\t\t\t   camera3_stream_t *camera3Stream, unsigned int index)\r\n+\t\t\t   camera3_stream_t *camera3Stream,\r\n+\t\t\t   CameraStream *const sourceStream, unsigned int index)\r\n \t: cameraDevice_(cameraDevice), config_(config), type_(type),\r\n-\t  camera3Stream_(camera3Stream), index_(index)\r\n+\t  camera3Stream_(camera3Stream), sourceStream_(sourceStream),\r\n+\t  index_(index)\r\n {\r\n }\r\n\r\n@@ -206,6 +208,12 @@ void CameraStream::flush()\r\n\r\n FrameBuffer *CameraStream::getBuffer()\r\n {\r\n+\tif (type_ == Type::Direct && !allocator_) {\r\n+\t\tallocator_ = std::make_unique<PlatformFrameBufferAllocator>(cameraDevice_);\r\n+\t\tASSERT(!mutex_);\r\n+\t\tmutex_ = std::make_unique<Mutex>();\r\n+\t}\r\n+\r\n \tif (!allocator_)\r\n \t\treturn nullptr;\r\n\r\ndiff --git a/src/android/camera_stream.h b/src/android/camera_stream.h\r\nindex f9504462..4c5078b2 100644\r\n--- a/src/android/camera_stream.h\r\n+++ b/src/android/camera_stream.h\r\n@@ -114,7 +114,9 @@ public:\r\n \t};\r\n \tCameraStream(CameraDevice *const cameraDevice,\r\n \t\t     libcamera::CameraConfiguration *config, Type type,\r\n-\t\t     camera3_stream_t *camera3Stream, unsigned int index);\r\n+\t\t     camera3_stream_t *camera3Stream,\r\n+\t\t     CameraStream *const sourceStream,\r\n+\t\t     unsigned int index);\r\n \tCameraStream(CameraStream &&other);\r\n \t~CameraStream();\r\n\r\n@@ -122,6 +124,7 @@ public:\r\n \tcamera3_stream_t *camera3Stream() const { return camera3Stream_; }\r\n \tconst libcamera::StreamConfiguration &configuration() const;\r\n \tlibcamera::Stream *stream() const;\r\n+\tCameraStream *sourceStream() const { return sourceStream_; }\r\n\r\n \tint configure();\r\n \tint process(Camera3RequestDescriptor::StreamBuffer *streamBuffer);\r\n@@ -167,6 +170,7 @@ private:\r\n \tconst libcamera::CameraConfiguration *config_;\r\n \tconst Type type_;\r\n \tcamera3_stream_t *camera3Stream_;\r\n+\tCameraStream *const sourceStream_;\r\n \tconst unsigned int index_;\r\n\r\n \tstd::unique_ptr<PlatformFrameBufferAllocator> allocator_;","prefixes":["libcamera-devel","1/1"]}